with caching for traversePath
<?php | |
# Copyright (c) 2003-2005, Jannis Hermanns (on behalf the Serendipity Developer Team) | |
# All rights reserved. See LICENSE file for licensing details | |
if (IN_serendipity !== true) { | |
die ("Don't hack!"); | |
} | |
/** | |
* Check if an uploaded file is "evil" | |
* | |
* @access public | |
* @param string Input filename | |
* @return boolean | |
*/ | |
function serendipity_isActiveFile($file) { | |
if (preg_match('@^\.@', $file)) { | |
return true; | |
} | |
$core = preg_match('@\.(php.*|[psj]html?|pht|aspx?|cgi|jsp|py|pl)$@i', $file); | |
if ($core) { | |
return true; | |
} | |
$eventData = false; | |
serendipity_plugin_api::hook_event('backend_media_check', $eventData, $file); | |
return $eventData; | |
} | |
/** | |
* Gets a list of media items from our media database | |
* | |
* LONG | |
* | |
* @access public | |
* @param int The offset to start fetching media files | |
* @param int How many items to fetch | |
* @param int The number (referenced variable) of fetched items | |
* @param string The "ORDER BY" sql part when fetching items | |
* @param string Order by DESC or ASC | |
* @param string Only fetch files from a specific directory | |
* @param string Only fetch specific filenames (including check for realname match) | |
* @param string Only fetch media with specific keyword | |
* @param array An array of restricting filter sets | |
* @param boolean Apply strict directory checks, or include subdirectories? | |
* @return array Resultset of images | |
*/ | |
function serendipity_fetchImagesFromDatabase($start=0, $limit=0, &$total=null, $order = false, $ordermode = false, $directory = '', $filename = '', $keywords = '', $filter = array(), $hideSubdirFiles = false) { | |
global $serendipity; | |
$cond = array( | |
'joinparts' => array(), | |
'parts' => array(), | |
); | |
$orderfields = serendipity_getImageFields(); | |
if (empty($order) || !isset($orderfields[$order])) { | |
$order = 'i.date'; | |
} | |
if (!is_array($filter)) { | |
$filter = array(); | |
} | |
if (empty($ordermode) || ($ordermode != 'DESC' && $ordermode != 'ASC')) { | |
$ordermode = 'DESC'; | |
} | |
if ($order == 'name') { | |
$order = 'realname ' . $ordermode . ', name'; | |
} | |
if ($limit != 0) { | |
$limitsql = serendipity_db_limit_sql(serendipity_db_limit($start, $limit)); | |
} | |
if ($hideSubdirFiles == false) { | |
if (!empty($directory)) { | |
$cond['parts']['directory'] = " AND i.path LIKE '" . serendipity_db_escape_string($directory) . "%'\n"; | |
} | |
// if in root, having no directory set, the query fetches all files by default, meaning we are done | |
} else { | |
if (!empty($directory)) { | |
$cond['parts']['directory'] = " AND i.path LIKE '" . serendipity_db_escape_string($directory) . "%'\n"; | |
} else { | |
$cond['parts']['directory'] = " AND i.path = ''\n"; | |
} | |
} | |
if (!empty($filename)) { | |
$cond['parts']['filename'] = " AND (i.name like '%" . serendipity_db_escape_string($filename) . "%' OR | |
i.realname like '%" . serendipity_db_escape_string($filename) . "%')\n"; | |
} | |
if (!is_array($keywords)) { | |
if (!empty($keywords)) { | |
$keywords = explode(';', $keywords); | |
} else { | |
$keywords = array(); | |
} | |
} | |
if (count($keywords) > 0) { | |
$cond['parts']['keywords'] = " AND (mk.property IN ('" . serendipity_db_implode("', '", $keywords, 'string') . "'))\n"; | |
$cond['joinparts']['keywords'] = true; | |
} | |
foreach($filter AS $f => $fval) { | |
if (! (isset($orderfields[$f]) || $f == "fileCategory") || empty($fval)) { | |
continue; | |
} | |
if (is_array($fval)) { | |
if (empty($fval['from']) || empty($fval['to'])) { | |
continue; | |
} | |
if ($orderfields[$f]['type'] == 'date') { | |
$fval['from'] = serendipity_convertToTimestamp(trim($fval['from'])); | |
$fval['to'] = serendipity_convertToTimestamp(trim($fval['to'])); | |
} | |
if (substr($f, 0, 3) === 'bp.') { | |
$realf = substr($f, 3); | |
$cond['parts']['filter'] .= " AND (bp2.property = '$realf' AND bp2.value >= " . (int)$fval['from'] . " AND bp2.value <= " . (int)$fval['to'] . ")\n"; | |
} else { | |
$cond['parts']['filter'] .= " AND ($f >= " . (int)$fval['from'] . " AND $f <= " . (int)$fval['to'] . ")\n"; | |
} | |
} elseif ($f == 'i.authorid') { | |
$cond['parts']['filter'] .= " AND ( | |
(hp.property = 'authorid' AND hp.value = " . (int)$fval . ") | |
OR | |
(i.authorid = " . (int)$fval . ") | |
)\n"; | |
$cond['joinparts']['hiddenproperties'] = true; | |
} elseif ($orderfields[$f]['type'] == 'int') { | |
if (substr($f, 0, 3) === 'bp.') { | |
$realf = substr($f, 3); | |
$cond['parts']['filter'] .= " AND (bp2.property = '$realf' AND bp2.value = '" . serendipity_db_escape_string(trim($fval)) . "')\n"; | |
} else { | |
$cond['parts']['filter'] .= " AND ($f = '" . serendipity_db_escape_string(trim($fval)) . "')\n"; | |
} | |
} elseif ($f == 'fileCategory') { | |
switch ($fval) { | |
case 'image': | |
$cond['parts']['filter'] .= " AND (i.mime LIKE 'image/%')\n"; | |
break; | |
case 'video': | |
$cond['parts']['filter'] .= " AND (i.mime LIKE 'video/%')\n"; | |
break; | |
} | |
} else { | |
if (substr($f, 0, 3) === 'bp.') { | |
$realf = substr($f, 3); | |
$cond['parts']['filter'] .= " AND (bp2.property = '$realf' AND bp2.value LIKE '%" . serendipity_db_escape_string(trim($fval)) . "%')\n"; | |
} else { | |
$cond['parts']['filter'] .= " AND ($f LIKE '%" . serendipity_db_escape_string(trim($fval)) . "%')\n"; | |
} | |
} | |
$cond['joinparts']['filterproperties'] = true; | |
} | |
if (isset($serendipity['authorid']) && !serendipity_checkPermission('adminImagesViewOthers')) { | |
$cond['parts']['authorid'] .= " AND (i.authorid = 0 OR i.authorid = " . (int)$serendipity['authorid'] . ")\n"; | |
} | |
$cond['and'] = 'WHERE 1=1 ' . implode("\n", $cond['parts']); | |
$cond['args'] = func_get_args(); | |
serendipity_plugin_api::hook_event('fetch_images_sql', $cond); | |
serendipity_ACL_SQL($cond, false, 'directory'); | |
if ($cond['joinparts']['keywords']) { | |
$cond['joins'] .= "\n LEFT OUTER JOIN {$serendipity['dbPrefix']}mediaproperties AS mk | |
ON (mk.mediaid = i.id AND mk.property_group = 'base_keyword')\n"; | |
} | |
if (substr($order, 0, 3) === 'bp.') { | |
$cond['orderproperty'] = substr($order, 3); | |
$cond['orderkey'] = 'bp.value'; | |
$order = 'bp.value'; | |
$cond['joinparts']['properties'] = true; | |
} else { | |
$cond['orderkey'] = "''"; | |
} | |
if ($cond['joinparts']['properties']) { | |
$cond['joins'] .= "\n LEFT OUTER JOIN {$serendipity['dbPrefix']}mediaproperties AS bp | |
ON (bp.mediaid = i.id AND bp.property_group = 'base_property' AND bp.property = '{$cond['orderproperty']}')\n"; | |
} | |
if ($cond['joinparts']['filterproperties']) { | |
$cond['joins'] .= "\n LEFT OUTER JOIN {$serendipity['dbPrefix']}mediaproperties AS bp2 | |
ON (bp2.mediaid = i.id AND bp2.property_group = 'base_property')\n"; | |
} | |
if ($cond['joinparts']['hiddenproperties']) { | |
$cond['joins'] .= "\n LEFT OUTER JOIN {$serendipity['dbPrefix']}mediaproperties AS hp | |
ON (hp.mediaid = i.id AND hp.property_group = 'base_hidden')\n"; | |
} | |
if ($serendipity['dbType'] == 'postgres' || | |
$serendipity['dbType'] == 'pdo-postgres') { | |
$cond['group'] = ''; | |
$cond['distinct'] = 'DISTINCT'; | |
} else { | |
$cond['group'] = 'GROUP BY i.id'; | |
$cond['distinct'] = ''; | |
} | |
$basequery = "FROM {$serendipity['dbPrefix']}images AS i | |
LEFT OUTER JOIN {$serendipity['dbPrefix']}authors AS a | |
ON i.authorid = a.authorid | |
{$cond['joins']} | |
{$cond['and']}"; | |
$query = "SELECT {$cond['distinct']} i.id, {$cond['orderkey']} AS orderkey, i.name, i.extension, i.mime, i.size, i.dimensions_width, i.dimensions_height, i.date, i.thumbnail_name, i.authorid, i.path, i.hotlink, i.realname, | |
a.realname AS authorname | |
$basequery | |
{$cond['group']} | |
ORDER BY $order $ordermode $limitsql"; | |
$rs = serendipity_db_query($query, false, 'assoc'); | |
if (!is_array($rs) && $rs !== true && $rs !== 1) { | |
echo '<div>' . $rs . '</div>'; | |
return array(); | |
} elseif (!is_array($rs)) { | |
return array(); | |
} | |
$total_query = "SELECT count(i.id) | |
$basequery | |
GROUP BY i.id"; | |
$total_rs = serendipity_db_query($total_query, false, 'num'); | |
if (is_array($total_rs)) { | |
$total = count($total_rs); | |
} | |
return $rs; | |
} | |
/** | |
* Fetch a specific media item from the mediadatabase | |
* | |
* @access public | |
* @param int The ID of an media item | |
* @return array The media info data | |
*/ | |
function serendipity_fetchImageFromDatabase($id, $mode = 'read') { | |
global $serendipity; | |
if (is_array($id)) { | |
$cond = array( | |
'and' => "WHERE i.id IN (" . serendipity_db_implode(',', $id) . ")" | |
); | |
$single = false; | |
$assocKey = 'id'; | |
$assocVal = false; | |
} else { | |
$cond = array( | |
'and' => "WHERE i.id = " . (int)$id | |
); | |
$single = true; | |
$assocKey = false; | |
$assocVal = false; | |
} | |
if ($serendipity['dbType'] == 'postgres' || | |
$serendipity['dbType'] == 'pdo-postgres') { | |
$cond['group'] = ''; | |
$cond['distinct'] = 'DISTINCT'; | |
} else { | |
$cond['group'] = 'GROUP BY i.id'; | |
$cond['distinct'] = ''; | |
} | |
if ($mode != 'discard') { | |
serendipity_ACL_SQL($cond, false, 'directory', $mode); | |
} | |
$rs = serendipity_db_query("SELECT {$cond['distinct']} i.id, i.name, i.extension, i.mime, i.size, i.dimensions_width, i.dimensions_height, i.date, i.thumbnail_name, i.authorid, i.path, i.hotlink, i.realname | |
FROM {$serendipity['dbPrefix']}images AS i | |
{$cond['joins']} | |
{$cond['and']} | |
{$cond['group']}", $single, 'assoc', false, $assocKey, $assocVal); | |
return $rs; | |
} | |
/** | |
* Update a media item | |
* | |
* @access public | |
* @param array An array of columns to update | |
* @param int The ID of an media item to update | |
* @return boolean | |
*/ | |
function serendipity_updateImageInDatabase($updates, $id) { | |
global $serendipity; | |
$admin = ''; | |
if (!serendipity_checkPermission('adminImagesAdd')) { | |
$admin = ' AND (authorid = ' . $serendipity['authorid'] . ' OR authorid = 0)'; | |
} | |
$i = 0; | |
if (sizeof($updates) > 0) { | |
$imageBeforeChange = serendipity_fetchImageFromDatabase($id); | |
foreach ($updates AS $k => $v) { | |
$q[] = $k ." = '" . serendipity_db_escape_string($v) . "'"; | |
} | |
serendipity_db_query("UPDATE {$serendipity['dbPrefix']}images SET ". implode(',', $q) ." WHERE id = " . (int)$id . " $admin"); | |
$i++; | |
// Check if this update changes important directory or filename attributes. | |
// If yes, the plugin API will forward this change of files to plugins like | |
// staticpage, so that they can update their contents. | |
if (isset($updates['path']) || isset($updates['realname'])) { | |
if (! isset($updates['path'])) { | |
$updates['path'] = $imageBeforeChange['path']; | |
} | |
if (! isset($updates['realname'])) { | |
$updates['realname'] = $imageBeforeChange['realname']; | |
} | |
// NOTE: Previously, the API supported multiple rename tasks like dir, filedir and file | |
// Now the core will ALWAYS propagate each file change distinctly, never will a directory | |
// be submitted to the API. | |
$eventData = array( // array in array because the event api expects that | |
array( | |
'type' => 'file', | |
'oldDir' => $imageBeforeChange['path'] . $imageBeforeChange['realname'], | |
'newDir' => $updates['path'] . $updates['realname'] | |
) | |
); | |
serendipity_plugin_api::hook_event('backend_media_rename', $eventData); | |
} | |
// If the user manually saved the image properties, some values might need updating now | |
// Name and realname are saved there as well, and the title is set to the name by default | |
if (isset($updates['realname'])) { | |
$q = "UPDATE {$serendipity['dbPrefix']}mediaproperties | |
SET value = '" . serendipity_db_escape_string($updates['realname']) . "' | |
WHERE mediaid = " . (int)$imageBeforeChange['id'] . ' AND property = "realname" AND value = "' . $imageBeforeChange['realname'] . '"'; | |
serendipity_db_query($q); | |
$q = "UPDATE {$serendipity['dbPrefix']}mediaproperties | |
SET value = '" . serendipity_db_escape_string($updates['realname']) . "' | |
WHERE mediaid = " . (int)$imageBeforeChange['id'] . ' AND property = "TITLE" AND value = "' . $imageBeforeChange['realname'] .'"'; | |
serendipity_db_query($q); | |
} | |
if (isset($updates['name'])) { | |
$q = "UPDATE {$serendipity['dbPrefix']}mediaproperties | |
SET value = '" . $updates['name'] . "' | |
WHERE mediaid = " . (int)$imageBeforeChange['id'] . ' AND property = "name" AND value = "' . $imageBeforeChange['name'] .'"'; | |
serendipity_db_query($q); | |
} | |
} | |
return $i; | |
} | |
/** | |
* Delete a media item | |
* | |
* @access public | |
* @param int The ID of a media item to delete | |
* @return | |
*/ | |
function serendipity_deleteImage($id) { | |
global $serendipity; | |
$dThumb = array(); | |
$messages = ''; | |
$file = serendipity_fetchImageFromDatabase($id); | |
if (!is_array($file)) { | |
$messages .= sprintf('<span class="msg_error"><span class="icon-attention-circled" aria-hidden="true"></span> ' . FILE_NOT_FOUND . "</span>\n", $id); | |
//return false; | |
} else { | |
$dFile = $file['path'] . $file['name'] . (empty($file['extension']) ? '' : '.' . $file['extension']); | |
$dThumb = array(array( | |
'fthumb' => $file['thumbnail_name'] | |
)); | |
if (!serendipity_checkPermission('adminImagesDelete')) { | |
return; | |
} | |
if (!serendipity_checkPermission('adminImagesMaintainOthers') && $file['authorid'] != '0' && $file['authorid'] != $serendipity['authorid']) { | |
// A non-admin user may not delete private files from other users. | |
return; | |
} | |
if (!$file['hotlink']) { | |
if (file_exists($serendipity['serendipityPath'] . $serendipity['uploadPath'] . $dFile)) { | |
if (@unlink($serendipity['serendipityPath'] . $serendipity['uploadPath'] . $dFile)) { | |
$messages .= sprintf('<span class="msg_success"><span class="icon-ok-circled" aria-hidden="true"></span> ' . DELETE_FILE . "</span>\n", $dFile); | |
} else { | |
$messages .= sprintf('<span class="msg_error"><span class="icon-attention-circled" aria-hidden="true"></span> ' . DELETE_FILE_FAIL . "</span>\n", $dFile); | |
} | |
serendipity_plugin_api::hook_event('backend_media_delete', $dThumb); | |
foreach($dThumb AS $thumb) { | |
$dfnThumb = $file['path'] . $file['name'] . (!empty($thumb['fthumb']) ? '.' . $thumb['fthumb'] : '') . (empty($file['extension']) ? '' : '.' . $file['extension']); | |
$dfThumb = $serendipity['serendipityPath'] . $serendipity['uploadPath'] . $dfnThumb; | |
if (@unlink($dfThumb)) { | |
$messages .= sprintf('<span class="msg_success"><span class="icon-ok-circled" aria-hidden="true"></span> ' . DELETE_THUMBNAIL . "</span>\n", $dfnThumb); | |
} | |
} | |
} else { | |
$messages .= sprintf('<span class="msg_error"><span class="icon-attention-circled" aria-hidden="true"></span> ' . FILE_NOT_FOUND . "</span>\n", $dFile); | |
} | |
} else { | |
$messages .= sprintf('<span class="msg_hint"><span class="icon-help-circled" aria-hidden="true"></span> ' . DELETE_HOTLINK_FILE . "</span>\n", $file['name']); | |
} | |
serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}images WHERE id = ". (int)$id); | |
serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}mediaproperties WHERE mediaid = ". (int)$id); | |
} | |
return $messages; | |
} | |
/** | |
* Open a directory and fetch all existing media items | |
* | |
* @access public | |
* @param boolean deprecated | |
* @param int deprecated | |
* @param int deprecated | |
* @param array Array list of found items | |
* @param string sub-directory to investigate [recursive use] | |
* @return array List of media items | |
*/ | |
function serendipity_fetchImages($group = false, $start = 0, $end = 20, $images = '', $odir = '') { | |
global $serendipity; | |
// Open directory | |
$basedir = $serendipity['serendipityPath'] . $serendipity['uploadPath']; | |
$images = array(); | |
if ($dir = @opendir($basedir . $odir)) { | |
$aTempArray = array(); | |
while (($file = @readdir($dir)) !== false) { | |
if ($file == '.svn' || $file == 'CVS' || $file == '.htaccess' || strtolower($file) == 'thumbs.db' || $file == '.' || $file == '..') { | |
continue; // 2013/06/05 added exclude .htaccess for ckeditor/kcfinder usage and 2013/12/25 added thumbs.db | |
} | |
array_push($aTempArray, $file); | |
} | |
@closedir($dir); | |
sort($aTempArray); | |
foreach($aTempArray AS $f) { | |
if (strpos($f, $serendipity['thumbSuffix']) !== false) { | |
// This is a s9y thumbnail, skip it. | |
continue; | |
} | |
$cdir = ($odir != '' ? $odir . '/' : ''); | |
if (is_dir($basedir . $odir . '/' . $f)) { | |
$temp = serendipity_fetchImages($group, $start, $end, $images, $cdir . $f); | |
foreach($temp AS $tkey => $tval) { | |
array_push($images, $tval); | |
} | |
} else { | |
array_push($images, $cdir . $f); | |
} | |
} | |
} | |
natsort($images); | |
/* BC */ | |
$serendipity['imageList'] = $images; | |
return $images; | |
} | |
/** | |
* Inserts a hotlinked media file | |
* | |
* hotlinks are files that are only linked in your database, and not really stored on your server | |
* | |
* @access public | |
* @param string The filename to hotlink | |
* @param string The URL to hotlink with | |
* @param int The owner of the hotlinked media item | |
* @param int The timestamp of insertion (unix second) | |
* @param string A temporary filename for fetching the file to investigate it | |
* @return int The ID of the inserted media item | |
*/ | |
function serendipity_insertHotlinkedImageInDatabase($filename, $url, $authorid = 0, $time = NULL, $tempfile = NULL) { | |
global $serendipity; | |
if (is_null($time)) { | |
$time = time(); | |
} | |
list($filebase, $extension) = serendipity_parseFileName($filename); | |
if ($tempfile && file_exists($tempfile)) { | |
$filesize = @filesize($tempfile); | |
$fdim = @serendipity_getimagesize($tempfile, '', $extension); | |
$width = $fdim[0]; | |
$height = $fdim[1]; | |
$mime = $fdim['mime']; | |
@unlink($tempfile); | |
} | |
$query = sprintf( | |
"INSERT INTO {$serendipity['dbPrefix']}images ( | |
name, | |
date, | |
authorid, | |
extension, | |
mime, | |
size, | |
dimensions_width, | |
dimensions_height, | |
path, | |
hotlink, | |
realname | |
) VALUES ( | |
'%s', | |
%s, | |
%s, | |
'%s', | |
'%s', | |
%s, | |
%s, | |
%s, | |
'%s', | |
1, | |
'%s' | |
)", | |
serendipity_db_escape_string($filebase), | |
(int)$time, | |
(int)$authorid, | |
serendipity_db_escape_string($extension), | |
serendipity_db_escape_string($mime), | |
(int)$filesize, | |
(int)$width, | |
(int)$height, | |
serendipity_db_escape_string($url), | |
serendipity_db_escape_string($filename) | |
); | |
$sql = serendipity_db_query($query); | |
if (is_string($sql)) { | |
echo '<span class="block_level">' . $query . "</span>\n"; | |
echo '<span class="block_level">' . $sql . "</span>\n"; | |
} | |
$image_id = serendipity_db_insert_id('images', 'id'); | |
if ($image_id > 0) { | |
return $image_id; | |
} | |
return 0; | |
} | |
/** | |
* Insert a media item in the database | |
* | |
* @access public | |
* @param string The filename of the media item | |
* @param string The path to the media item | |
* @param int The owner author of the item | |
* @param int The timestamp of when the media item was inserted | |
* @return int The new media ID | |
*/ | |
function serendipity_insertImageInDatabase($filename, $directory, $authorid = 0, $time = NULL, $realname = NULL) { | |
global $serendipity; | |
if (is_null($time)) { | |
$time = time(); | |
} | |
if (is_null($realname)) { | |
$realname = $filename; | |
} | |
$filepath = $serendipity['serendipityPath'] . $serendipity['uploadPath'] . $directory . $filename; | |
$filesize = @filesize($filepath); | |
list($filebase, $extension) = serendipity_parseFileName($filename); | |
$thumbpath = $serendipity['serendipityPath'] . $serendipity['uploadPath'] . $directory . $filebase . '.'. $serendipity['thumbSuffix'] . (empty($extension) ? '' : '.' . $extension); | |
$thumbnail = (file_exists($thumbpath) ? $serendipity['thumbSuffix'] : ''); | |
$fdim = @serendipity_getimagesize($filepath, '', $extension); | |
$width = $fdim[0]; | |
$height = $fdim[1]; | |
$mime = $fdim['mime']; | |
$query = sprintf( | |
"INSERT INTO {$serendipity['dbPrefix']}images ( | |
name, | |
extension, | |
mime, | |
size, | |
dimensions_width, | |
dimensions_height, | |
thumbnail_name, | |
date, | |
authorid, | |
path, | |
realname | |
) VALUES ( | |
'%s', | |
'%s', | |
'%s', | |
%s, | |
%s, | |
%s, | |
'%s', | |
%s, | |
%s, | |
'%s', | |
'%s' | |
)", | |
serendipity_db_escape_string($filebase), | |
serendipity_db_escape_string($extension), | |
serendipity_db_escape_string($mime), | |
(int)$filesize, | |
(int)$width, | |
(int)$height, | |
serendipity_db_escape_string($thumbnail), | |
(int)$time, | |
(int)$authorid, | |
serendipity_db_escape_string($directory), | |
serendipity_db_escape_string($realname) | |
); | |
$sql = serendipity_db_query($query); | |
if (is_string($sql)) { | |
echo '<span class="block_level">' . $query . "</span>\n"; | |
echo '<span class="block_level">' . $sql . "</span>\n"; | |
} | |
$image_id = serendipity_db_insert_id('images', 'id'); | |
if ($image_id > 0) { | |
return $image_id; | |
serendipity_cleanCache(); | |
} | |
return 0; | |
} | |
/** | |
* Create a thumbnail for an image | |
* | |
* LONG | |
* | |
* @access public | |
* @param string The input image filename | |
* @param string The directory to the image file | |
* @param string The target size of the thumbnail (2-dimensional array width,height) | |
* @param string Name of the thumbnail | |
* @param bool Store thumbnail in temporary place? | |
* @param bool Force enlarging of small images? | |
* @return array The result size of the thumbnail | |
*/ | |
function serendipity_makeThumbnail($file, $directory = '', $size = false, $thumbname = false, $is_temporary = false, $force_resize = false) { | |
global $serendipity; | |
if ($size === false) { | |
$size = $serendipity['thumbSize']; | |
} | |
if ($size < 1) { | |
return array(0,0); | |
} | |
if ($thumbname === false) { | |
$thumbname = $serendipity['thumbSuffix']; | |
} | |
$t = serendipity_parseFileName($file); | |
$f = $t[0]; | |
$suf = $t[1]; | |
$infile = $serendipity['serendipityPath'] . $serendipity['uploadPath'] . $directory . $file; | |
#echo 'From: ' . $infile . '<br />'; | |
if ($is_temporary) { | |
$temppath = dirname($thumbname); | |
if (!is_dir($temppath)) { | |
@mkdir($temppath); | |
} | |
$outfile = $thumbname; | |
} else { | |
$outfile = $serendipity['serendipityPath'] . $serendipity['uploadPath'] . $directory . $f . '.' . $thumbname . '.' . $suf; | |
} | |
$serendipity['last_outfile'] = $outfile; | |
#echo 'To: ' . $outfile . '<br />'; | |
$fdim = @serendipity_getimagesize($infile, '', $suf); | |
if (isset($fdim['noimage'])) { | |
$r = array(0, 0); | |
} else { | |
if ($serendipity['magick'] !== true) { | |
if (is_array($size)) { | |
// The caller wants a thumbnail with a specific size | |
$r = serendipity_resize_image_gd($infile, $outfile, $size['width'], $size['height']); | |
} else { | |
// The caller wants a thumbnail constrained in the dimension set by config | |
$calc = serendipity_calculate_aspect_size($fdim[0], $fdim[1], $size, $serendipity['thumbConstraint']); | |
$r = serendipity_resize_image_gd($infile, $outfile, $calc[0], $calc[1]); | |
} | |
} else { | |
if (is_array($size)) { | |
if ($fdim[0] > $size['width'] && $fdim[1] > $size['height']) { | |
$r = $size; | |
} else { | |
return array(0,0); // do not create any thumb, if image is smaller than defined sizes | |
} | |
} else { | |
$calc = serendipity_calculate_aspect_size($fdim[0], $fdim[1], $size, $serendipity['thumbConstraint']); | |
$r = array('width' => $calc[0], 'height' => $calc[1]); | |
} | |
$newSize = $r['width'] . 'x' . $r['height']; | |
if ($fdim['mime'] == 'application/pdf') { | |
$cmd = escapeshellcmd($serendipity['convert']) . ' -antialias -flatten -scale '. serendipity_escapeshellarg($newSize) .' '. serendipity_escapeshellarg($infile . '[0]') . ' ' . serendipity_escapeshellarg($outfile . '.png'); | |
} else { | |
if (!$force_resize && serendipity_ini_bool(ini_get('safe_mode')) === false) { | |
$newSize .= '>'; // tell imagemagick to not enlarge small images, only works if safe_mode is off (safe_mode turns > in to \>) | |
} | |
if (!$serendipity['imagemagick_nobang']) $newSize .= '!'; // force the first run image geometry exactly to given sizes, if there were rounding differences (see https://github.com/s9y/Serendipity/commit/94881ba4c0e3bdd4b5fac510e93977e239171c1c and comments) | |
$cmd = escapeshellcmd($serendipity['convert'] . ' ' . $serendipity['imagemagick_thumb_parameters']) . ' -antialias -resize ' . serendipity_escapeshellarg($newSize) . ' ' . serendipity_escapeshellarg($infile) .' '. serendipity_escapeshellarg($outfile); | |
} | |
exec($cmd, $output, $result); | |
if ($result != 0) { | |
echo '<span class="msg_error"><span class="icon-attention-circled" aria-hidden="true"></span> ' . sprintf(IMAGICK_EXEC_ERROR, $cmd, $output[0], $result) ."</span>\n"; | |
$r = false; // return failure | |
} else { | |
touch($outfile); | |
} | |
unset($output, $result); | |
} | |
} | |
return $r; | |
} | |
/** | |
* Scale an image | |
* | |
* LONG | |
* | |
* @access public | |
* @param int The ID of an image | |
* @param int The target width | |
* @param int The target height | |
* @return true | |
*/ | |
function serendipity_scaleImg($id, $width, $height) { | |
global $serendipity; | |
$file = serendipity_fetchImageFromDatabase($id); | |
if (!is_array($file)) { | |
return false; | |
} | |
$admin = ''; | |
if (!serendipity_checkPermission('adminImagesMaintainOthers') && $file['authorid'] != '0' && $file['authorid'] != $serendipity['authorid']) { | |
return; | |
} | |
$infile = $outfile = $serendipity['serendipityPath'] . $serendipity['uploadPath'] . $file['path'] . $file['name'] . (empty($file['extension']) ? '' : '.' . $file['extension']); | |
if ($serendipity['magick'] !== true) { | |
serendipity_resize_image_gd($infile, $outfile, $width, $height); | |
} else { | |
$cmd = escapeshellcmd($serendipity['convert']) . ' -scale ' . serendipity_escapeshellarg($width . 'x' . $height) . ' ' . serendipity_escapeshellarg($infile) . ' ' . serendipity_escapeshellarg($outfile); | |
exec($cmd, $output, $result); | |
if ( $result != 0 ) { | |
echo '<span class="msg_error"><span class="icon-attention-circled" aria-hidden="true"></span> ' . sprintf(IMAGICK_EXEC_ERROR, $cmd, $output[0], $result) ."</span>\n"; | |
return false; | |
} | |
unset($output, $result); | |
} | |
if ($result == 0) { | |
serendipity_updateImageInDatabase(array('dimensions_width' => $width, 'dimensions_height' => $height, 'size' => @filesize($outfile)), $id); | |
return true; | |
} | |
return false; | |
} | |
/** | |
* Rotate an image | |
* | |
* LONG | |
* | |
* @access public | |
* @param int The ID of an image | |
* @param int Number of degrees to rotate | |
* @return boolean | |
*/ | |
function serendipity_rotateImg($id, $degrees) { | |
global $serendipity; | |
$file = serendipity_fetchImageFromDatabase($id); | |
if (!is_array($file)) { | |
return false; | |
} | |
$admin = ''; | |
if (!serendipity_checkPermission('adminImagesMaintainOthers') && $file['authorid'] != '0' && $file['authorid'] != $serendipity['authorid']) { | |
// A non-admin user may not delete private files from other users. | |
return false; | |
} | |
$infile = $outfile = $serendipity['serendipityPath'] . $serendipity['uploadPath'] . $file['path'] . $file['name'] . (empty($file['extension']) ? '' : '.' . $file['extension']); | |
$thumbnails = serendipity_getThumbnails($id); | |
if ($serendipity['magick'] !== true) { | |
serendipity_rotate_image_gd($infile, $outfile, $degrees); | |
foreach($thumbnails as $thumbnail) { | |
$infileThumb = $outfileThumb = $thumbnail; | |
serendipity_rotate_image_gd($infileThumb, $outfileThumb, $degrees); | |
} | |
} else { | |
/* Why can't we just all agree on the rotation direction? */ | |
$degrees = (360 - $degrees); | |
/* rotate main image */ | |
$cmd = escapeshellcmd($serendipity['convert']) . ' -rotate ' . serendipity_escapeshellarg($degrees) . ' ' . serendipity_escapeshellarg($infile) . ' ' . serendipity_escapeshellarg($outfile); | |
exec($cmd, $output, $result); | |
if ( $result != 0 ) { | |
echo '<span class="msg_error"><span class="icon-attention-circled" aria-hidden="true"></span> ' . sprintf(IMAGICK_EXEC_ERROR, $cmd, $output[0], $result) ."</span>\n"; | |
} | |
unset($output, $result); | |
/* rotate thumbnail */ | |
foreach($thumbnails as $thumbnail) { | |
$infileThumb = $outfileThumb = $thumbnail; | |
$cmd = escapeshellcmd($serendipity['convert']) . ' -rotate ' . serendipity_escapeshellarg($degrees) . ' ' . serendipity_escapeshellarg($infileThumb) . ' ' . serendipity_escapeshellarg($outfileThumb); | |
exec($cmd, $output, $result); | |
if ( $result != 0 ) { | |
echo '<span class="msg_error"><span class="icon-attention-circled" aria-hidden="true"></span> ' . sprintf(IMAGICK_EXEC_ERROR, $cmd, $output[0], $result) ."</span>\n"; | |
} | |
unset($output, $result); | |
} | |
} | |
$fdim = @getimagesize($outfile); | |
serendipity_updateImageInDatabase(array('dimensions_width' => $fdim[0], 'dimensions_height' => $fdim[1]), $id); | |
return true; | |
} | |
/** | |
* Creates thumbnails for all images in the upload dir | |
* | |
* @access public | |
* @return int Number of created thumbnails | |
*/ | |
function serendipity_generateThumbs() { | |
global $serendipity; | |
$i = 0; | |
$serendipity['imageList'] = serendipity_fetchImagesFromDatabase(0, 0, $total); | |
$msg_printed = false; | |
foreach ($serendipity['imageList'] AS $k => $file) { | |
$is_image = serendipity_isImage($file); | |
if ($is_image && !$file['hotlink']) { | |
$update = false; | |
$filename = $file['path'] . $file['name'] . (empty($file['extension']) ? '' : '.' . $file['extension']); | |
$ffull = $serendipity['serendipityPath'] . $serendipity['uploadPath'] . $filename; | |
if (!file_exists($ffull)) { | |
serendipity_deleteImage($file['id']); | |
continue; | |
} | |
if (empty($file['thumbnail_name'])) { | |
$file['thumbnail_name'] = $serendipity['thumbSuffix']; | |
} | |
$oldThumb = $serendipity['serendipityPath'] . $serendipity['uploadPath'] . $file['path'] . $file['name'] . '.' . $file['thumbnail_name'] . (empty($file['extension']) ? '' : '.' . $file['extension']); | |
$newThumb = $serendipity['serendipityPath'] . $serendipity['uploadPath'] . $file['path'] . $file['name'] . '.' . $serendipity['thumbSuffix'] . (empty($file['extension']) ? '' : '.' . $file['extension']); | |
$sThumb = $file['path'] . $file['name'] . '.' . $serendipity['thumbSuffix'] . (empty($file['extension']) ? '' : '.' . $file['extension']); | |
$fdim = @getimagesize($ffull); | |
if (!file_exists($oldThumb) && !file_exists($newThumb) && ($fdim[0] > $serendipity['thumbSize'] || $fdim[1] > $serendipity['thumbSize'])) { | |
$returnsize = serendipity_makeThumbnail($file['name'] . (empty($file['extension']) ? '' : '.' . $file['extension']), $file['path']); | |
if ($returnsize !== false ) { | |
// Only print the resize message the first time | |
if (!$msg_printed) { | |
$resizemedia = sprintf(RESIZE_BLAHBLAH, THUMBNAIL_SHORT); | |
printf('<span class="msg_notice"><span class="icon-info-circled" aria-hidden="true"></span> ' . $resizemedia . "</span>\n"); | |
echo "\n" . '<ul class="serendipityFileList">' . "\n"; | |
$msg_printed = true; | |
} | |
echo '<li>' . $sThumb . ': ' . $returnsize['width'] . 'x' . $returnsize['height'] . "</li>\n"; | |
if (!file_exists($newThumb)) { | |
printf('<li><span class="msg_error"><span class="icon-attention-circled" aria-hidden="true"></span> ' . THUMBNAIL_FAILED_COPY . '</span></li>' . "\n", $sThumb); | |
} else { | |
$update = true; | |
} | |
} | |
} elseif (!file_exists($oldThumb) && !file_exists($newThumb) && $fdim[0] <= $serendipity['thumbSize'] && $fdim[1] <= $serendipity['thumbSize']) { | |
if (!$msg_printed) { | |
$resizethumb = sprintf(RESIZE_BLAHBLAH, THUMB); | |
printf('<span class="msg_notice"><span class="icon-info-circled" aria-hidden="true"></span> ' . $resizethumb . "</span>\n"); | |
echo "\n" . '<ul class="serendipityFileList">' . "\n"; | |
$msg_printed = true; | |
} | |
$res = @copy($ffull, $newThumb); | |
if (@$res === true) { | |
printf('<li>' . THUMBNAIL_USING_OWN . '</li>' . "\n", $sThumb); | |
$update = true; | |
} else { | |
printf('<li><span class="msg_error"><span class="icon-attention-circled" aria-hidden="true"></span> ' . THUMBNAIL_FAILED_COPY . '</span></li>' . "\n", $sThumb); | |
} | |
} | |
if ($update) { | |
$i++; | |
$updates = array('thumbnail_name' => $serendipity['thumbSuffix']); | |
serendipity_updateImageInDatabase($updates, $file['id']); | |
} | |
} else { | |
// Currently, non-image files have no thumbnail. | |
} | |
} | |
// Close the list, if it was created | |
if ($msg_printed) { | |
echo "</ul>\n"; | |
} | |
return $i; | |
} | |
/** | |
* Guess the MIME type of a file | |
* | |
* @access public | |
* @param string Filename extension | |
* @return string Mimetype | |
*/ | |
function serendipity_guessMime($extension) { | |
$mime = ''; | |
switch (strtolower($extension)) { | |
case 'jpg': | |
case 'jpeg': | |
$mime = 'image/jpeg'; | |
break; | |
case 'aiff': | |
case 'aif': | |
$mime = 'audio/x-aiff'; | |
break; | |
case 'gif': | |
$mime = 'image/gif'; | |
break; | |
case 'png': | |
$mime = 'image/png'; | |
break; | |
case 'pdf': | |
$mime = 'application/pdf'; | |
break; | |
case 'doc': | |
$mime = 'application/msword'; | |
break; | |
case 'rtf': | |
$mime = 'application/rtf'; | |
break; | |
case 'wav': | |
case 'wave': | |
$mime = 'audio/x-wav'; | |
break; | |
case 'mp2': | |
case 'mpg': | |
case 'mpeg': | |
$mime = 'video/x-mpeg'; | |
break; | |
case 'avi': | |
$mime = 'video/x-msvideo'; | |
break; | |
case 'mp3': | |
$mime = 'audio/x-mpeg3'; | |
break; | |
case 'xlm': | |
case 'xlb': | |
case 'xll': | |
case 'xla': | |
case 'xlw': | |
case 'xlc': | |
case 'xls': | |
case 'xlt': | |
$mime = 'application/vnd.ms-excel'; | |
break; | |
case 'ppt': | |
case 'pps': | |
$mime = 'application/vnd.ms-powerpoint'; | |
break; | |
case 'html': | |
case 'htm': | |
$mime = 'text/html'; | |
break; | |
case 'xsl': | |
case 'xslt': | |
case 'xml': | |
case 'wsdl': | |
case 'xsd': | |
$mime = 'text/xml'; | |
break; | |
case 'zip': | |
$mime = 'application/zip'; | |
break; | |
case 'tar': | |
$mime = 'application/x-tar'; | |
break; | |
case 'tgz': | |
case 'gz': | |
$mime = 'application/x-gzip'; | |
break; | |
case 'swf': | |
$mime = 'application/x-shockwave-flash'; | |
break; | |
case 'rm': | |
case 'ra': | |
case 'ram': | |
$mime = 'application/vnd.rn-realaudio'; | |
break; | |
case 'exe': | |
$mime = 'application/octet-stream'; | |
break; | |
case 'mov': | |
case 'mp4': | |
case 'qt': | |
$mime = 'video/x-quicktime'; | |
break; | |
case 'midi': | |
case 'mid': | |
$mime = 'audio/x-midi'; | |
break; | |
case 'txt': | |
$mime = 'text/plain'; | |
break; | |
case 'qcp': | |
$mime = 'audio/vnd.qcelp'; | |
break; | |
case 'emf': | |
$mime = 'image/x-emf'; | |
break; | |
case 'wmf': | |
$mime = 'image/x-wmf'; | |
break; | |
case 'snd': | |
$mime = 'audio/basic'; | |
break; | |
case 'pmd': | |
$mime = 'application/x-pmd'; | |
break; | |
case 'wbmp': | |
$mime = 'image/vnd.wap.wbmp'; | |
break; | |
case 'gcd': | |
$mime = 'text/x-pcs-gcd'; | |
break; | |
case 'mms': | |
$mime = 'application/vnd.wap.mms-message'; | |
break; | |
case 'ogg': | |
case 'ogm': | |
$mime = 'application/ogg'; | |
break; | |
case 'rv': | |
$mime = 'video/vnd.rn-realvideo'; | |
break; | |
case 'wmv': | |
$mime = 'video/x-ms-wmv'; | |
break; | |
case 'wma': | |
$mime = 'audio/x-ms-wma'; | |
break; | |
case 'qcp': | |
$mime = 'audio/vnd.qcelp'; | |
break; | |
case 'jad': | |
$mime = 'text/vnd.sun.j2me.app-descriptor'; | |
break; | |
case '3g2': | |
case '3gp': | |
$mime = 'video/3gpp'; | |
break; | |
case 'jar': | |
$mime = 'application/java-archive'; | |
break; | |
case 'ico': | |
$mime = 'image/x-icon'; | |
break; | |
case 'svg': | |
$mime = 'image/svg'; | |
break; | |
default: | |
$mime = 'application/octet-stream'; | |
break; | |
} | |
return $mime; | |
} | |
/** | |
* Check all existing thumbnails if they are the right size, insert missing thumbnails | |
* | |
* LONG | |
* | |
* @access public | |
* @return int Number of updated thumbnails | |
*/ | |
function serendipity_syncThumbs($deleteThumbs = false) { | |
global $serendipity; | |
$i = 0; | |
$files = serendipity_fetchImages(); | |
$fcount = count($files); | |
for ($x = 0; $x < $fcount; $x++) { | |
$update = array(); | |
$f = serendipity_parseFileName($files[$x]); | |
if (empty($f[1]) || $f[1] == $files[$x]) { | |
// No extension means bad file most probably. Skip it. | |
printf('<span class="msg_error"><span class="icon-attention-circled" aria-hidden="true"></span> ' . SKIPPING_FILE_EXTENSION . "</span>\n", $files[$x]); | |
continue; | |
} | |
$ffull = $serendipity['serendipityPath'] . $serendipity['uploadPath'] . $files[$x]; | |
$fthumb = $serendipity['serendipityPath'] . $serendipity['uploadPath'] . $f[0] . '.' . $serendipity['thumbSuffix'] . '.' . $f[1]; | |
$sThumb = $f[0] . '.' . $serendipity['thumbSuffix'] . '.' . $f[1]; | |
$fbase = basename($f[0]); | |
$fdir = dirname($f[0]) . '/'; | |
if ($fdir == './') { | |
$fdir = ''; | |
} | |
if (!is_readable($ffull) || filesize($ffull) == 0) { | |
printf('<span class="msg_error"><span class="icon-attention-circled" aria-hidden="true"></span> ' . SKIPPING_FILE_UNREADABLE . "</span>\n", $files[$x]); | |
continue; | |
} | |
$ft_mime = serendipity_guessMime($f[1]); | |
$fdim = serendipity_getimagesize($ffull, $ft_mime); | |
// If we're supposed to delete thumbs, this is the easiest place. Leave messages plain unstiled. | |
if (is_readable($fthumb)) { | |
if ($deleteThumbs === true) { | |
if (@unlink($fthumb)) { | |
printf(DELETE_THUMBNAIL . "<br />\n", $sThumb); | |
$i++; | |
} | |
} else if ($deleteThumbs == 'checksize') { | |
// Find existing thumbnail dimensions | |
$tdim = serendipity_getimagesize($fthumb); | |
if ($tdim['noimage']) { | |
// Delete it so it can be regenerated | |
if (@unlink($fthumb)) { | |
printf(DELETE_THUMBNAIL . "<br />\n", $sThumb); | |
$i++; | |
} | |
} else { | |
// Calculate correct thumbnail size from original image | |
$expect = serendipity_calculate_aspect_size($fdim[0], $fdim[1], $serendipity['thumbSize'], $serendipity['thumbConstraint']); | |
// Check actual thumbnail size | |
if ($tdim[0] != $expect[0] || $tdim[1] != $expect[1]) { | |
// This thumbnail is incorrect; delete it so | |
// it can be regenerated | |
if (@unlink($fthumb)) { | |
printf(DELETE_THUMBNAIL . "<br />\n", $sThumb); | |
$i++; | |
} | |
} | |
} | |
} | |
// else the option is to keep all existing thumbs; do nothing. | |
} // end if thumb exists | |
$cond = array( | |
'and' => "WHERE name = '" . serendipity_db_escape_string($fbase) . "' | |
" . ($fdir != '' ? "AND path = '" . serendipity_db_escape_string($fdir) . "'" : '') . " | |
AND mime = '" . serendipity_db_escape_string($fdim['mime']) . "' | |
AND extension = '" . serendipity_db_escape_string($f[1]) . "'" | |
); | |
serendipity_ACL_SQL($cond, false, 'directory'); | |
$rs = serendipity_db_query("SELECT * | |
FROM {$serendipity['dbPrefix']}images AS i | |
{$cond['joins']} | |
{$cond['and']}", true, 'assoc'); | |
// Leave messages plain unstiled | |
if (is_array($rs)) { | |
// This image is in the database. Check our calculated data against the database data. | |
$update = array(); | |
// Is the width correct? | |
if (isset($fdim[0]) && $rs['dimensions_width'] != $fdim[0]) { | |
$update['dimensions_width'] = $fdim[0]; | |
} | |
// Is the height correct? | |
if (isset($fdim[1]) && $rs['dimensions_height'] != $fdim[1]) { | |
$update['dimensions_height'] = $fdim[1]; | |
} | |
// Is the image size correct? | |
if ($rs['size'] != filesize($ffull)) { | |
$update['size'] = filesize($ffull); | |
} | |
// Has the thumbnail suffix changed? | |
$checkfile = $serendipity['serendipityPath'] . $serendipity['uploadPath'] . $rs['path'] . $rs['name'] . '.' . $rs['thumbnail_name'] . (empty($rs['extension']) ? '' : '.' . $rs['extension']); | |
if (!file_exists($checkfile) && file_exists($fthumb)) { | |
$update['thumbnail_name'] = $serendipity['thumbSuffix']; | |
} | |
/* Do the database update, if needed */ | |
if (sizeof($update) != 0) { | |
printf(FOUND_FILE . ' (2)<br />', $files[$x]); | |
serendipity_updateImageInDatabase($update, $rs['id']); | |
$i++; | |
} | |
} else { | |
printf(FOUND_FILE . ' (1)<br />', $files[$x]); | |
serendipity_insertImageInDatabase($fbase . '.' . $f[1], $fdir, 0, filemtime($ffull)); | |
$i++; | |
} | |
} | |
return $i; | |
} | |
/** | |
* Wrapper for GDLib functions | |
* | |
* @access public | |
* @param string Filename to operate on | |
* @return string Functionname to execute | |
*/ | |
function serendipity_functions_gd($infilename) { | |
if (!function_exists('imagecopyresampled')) { | |
return false; | |
} | |
$func = array(); | |
$inf = pathinfo(strtolower($infilename)); | |
switch ($inf['extension']) { | |
case 'gif': | |
$func['load'] = 'imagecreatefromgif'; | |
$func['save'] = 'imagegif'; | |
$func['qual'] = 100; | |
break; | |
case 'jpeg': | |
case 'jpg': | |
case 'jfif': | |
$func['load'] = 'imagecreatefromjpeg'; | |
$func['save'] = 'imagejpeg'; | |
$func['qual'] = 100; | |
break; | |
case 'png': | |
$func['load'] = 'imagecreatefrompng'; | |
$func['save'] = 'imagepng'; | |
$func['qual'] = 9; | |
break; | |
default: | |
return false; | |
} | |
/* If our loader does not exist, we are doomed */ | |
if (!function_exists($func['load'])) { | |
return false; | |
} | |
/* If the save function does not exist (i.e. read-only GIF), we want to output it as PNG */ | |
if (!function_exists($func['save'])) { | |
if (function_exists('imagepng')) { | |
$func['save'] = 'imagepng'; | |
} else { | |
return false; | |
} | |
} | |
return $func; | |
} | |
/** | |
* Rotate an image (GDlib) | |
* | |
* @access public | |
* @param string Source Filename to rotate | |
* @param string Target file | |
* @param int Degress to rotate | |
* @return array New width/height of the image | |
*/ | |
function serendipity_rotate_image_gd($infilename, $outfilename, $degrees) | |
{ | |
$func = serendipity_functions_gd($infilename); | |
if (!is_array($func)) { | |
return false; | |
} | |
$in = $func['load']($infilename); | |
$out = imagerotate($in, $degrees, 0); | |
$func['save']($out, $outfilename, $func['qual']); | |
$newwidth = imagesx($out); | |
$newheight = imagesy($out); | |
$out = null; | |
$in = null; | |
return array($newwidth, $newheight); | |
} | |
/** | |
* Resize an image (GDLib) | |
* | |
* @access public | |
* @param string Source Filename to resize | |
* @param string Target file | |
* @param int New width | |
* @return int New height (can be autodetected) | |
* @return array New image size | |
*/ | |
function serendipity_resize_image_gd($infilename, $outfilename, $newwidth, $newheight=null) | |
{ | |
$func = serendipity_functions_gd($infilename); | |
if (!is_array($func)) { | |
return false; | |
} | |
try { | |
// if an image exist that can not be loaded (invalid gif for example), the page shall still be rendered | |
$in = $func['load']($infilename); | |
} catch (Exception $e) { | |
echo 'Could not create thumbnail: ', $e->getMessage(), "\n"; | |
return false; | |
} | |
$width = imagesx($in); | |
$height = imagesy($in); | |
if (is_null($newheight)) { | |
$newsizes = serendipity_calculate_aspect_size($width, $height, $newwidth, 'width'); | |
$newwidth = $newsizes[0]; | |
$newheight = $newsizes[1]; | |
} | |
if (is_null($newwidth)) { | |
$newsizes = serendipity_calculate_aspect_size($width, $height, $newheight, 'height'); | |
$newwidth = $newsizes[0]; | |
$newheight = $newsizes[1]; | |
} | |
$out = imagecreatetruecolor($newwidth, $newheight); | |
/* Attempt to copy transparency information, this really only works for PNG */ | |
if (function_exists('imagesavealpha')) { | |
imagealphablending($out, false); | |
imagesavealpha($out, true); | |
} | |
imagecopyresampled($out, $in, 0, 0, 0, 0, $newwidth, $newheight, $width, $height); | |
@umask(0000); | |
touch($outfilename); // safe_mode requirement | |
$func['save']($out, $outfilename, $func['qual']); | |
@chmod($outfilename, 0664); | |
$out = null; | |
$in = null; | |
return array($newwidth, $newheight); | |
} | |
/** | |
* Calculate new size for an image, considering aspect ratio and constraint | |
* | |
* @access public | |
* @param int Image width | |
* @param int Image height | |
* @param int Target dimension size | |
* @param string Dimension to constrain ('width', 'height', 'largest', | |
* 'smallest'; defaults to original behavior, 'largest') | |
* @return array An array with the scaled width and height | |
*/ | |
function serendipity_calculate_aspect_size($width, $height, $size, $constraint = null) { | |
// Allow for future constraints (idea: 'percent') | |
$known_constraints = array('width', 'height', 'largest', 'smallest'); | |
// Rearrange params for calls from old imageselectorplus plugin | |
if ($size == null) { | |
$size = $constraint; | |
$constraint = 'smallest'; | |
} | |
// Normalize relative constraint types | |
if ($constraint == 'largest' || !in_array($constraint, $known_constraints)) { | |
// Original default behavior, included for backwards compatibility | |
// Constrains largest dimension | |
if ($width >= $height) { | |
$constraint = 'width'; | |
} else { | |
$constraint = 'height'; | |
} | |
} else if ($constraint == 'smallest') { | |
// Only ever called from imageselectorplus plugin, included for | |
// backwards compatibility with its older versions | |
if ($width >= $height) { | |
$constraint = 'height'; | |
} else { | |
$constraint = 'width'; | |
} | |
} | |
// Constraint is now definitely one of the known absolute types, | |
// either 'width' or 'height' | |
if ($constraint == 'height') { | |
// Is the image big enough to resize? | |
if ($height > $size) { | |
// Calculate new size | |
$ratio = $width / $height; | |
$newwidth = round($size * $ratio); | |
// Limit calculated dimension to at least 1px | |
if ($newwidth <= 0) { | |
$newwidth = 1; | |
} | |
$newsize = array($newwidth, $size); | |
} else { | |
// Image is too small to be resized; use original dimensions | |
$newsize = array($width, $height); | |
} | |
} else { | |
// Default constraint is width | |
if ($width > $size) { | |
// Image is big enough to resize | |
$ratio = $height / $width; | |
$newheight = round($size * $ratio); | |
// Limit calculated dimension to at least 1px | |
if ($newheight <= 0) { | |
$newheight = 1; | |
} | |
$newsize = array($size, $newheight); | |
} else { | |
// Do not scale small images | |
$newsize = array($width, $height); | |
} | |
} | |
return $newsize; | |
} | |
/** | |
* Display the list of images in our database | |
* | |
* @access public | |
* @param int The current page number | |
* @param string The HTML linebreak to use after a row of images | |
* @param boolean Is this the ML-Version for managing everything (true), or is it about selecting one image for the editor? (false) | |
* @param string The URL to use for pagination | |
* @param boolean Show the "upload media item" feature? | |
* @param boolean Restrict viewing images to a specific directory | |
* @param array Map of smarty vars transported into all following templates | |
* @return string Generated HTML | |
*/ | |
function serendipity_displayImageList($page = 0, $lineBreak = NULL, $manage = false, $url = NULL, $show_upload = false, $limit_path = NULL, $smarty_vars = array()) { | |
global $serendipity; | |
static $debug = false; | |
$extraParems = serendipity_generateImageSelectorParems(); | |
$serendipity['GET']['only_path'] = serendipity_uploadSecure($limit_path . $serendipity['GET']['only_path'], true); | |
$serendipity['GET']['only_filename'] = serendipity_specialchars(str_replace(array('*', '?'), array('%', '_'), $serendipity['GET']['only_filename'])); | |
$perPage = (!empty($serendipity['GET']['sortorder']['perpage']) ? (int)$serendipity['GET']['sortorder']['perpage'] : 8); | |
while ($perPage % $lineBreak !== 0) { | |
$perPage++; | |
} | |
$start = ($page-1) * $perPage; | |
if ($manage && $limit_path == NULL) { | |
## SYNCH START ## | |
$aExclude = array("CVS" => true, ".svn" => true, "_vti_cnf" => true); // _vti_cnf to exclude possible added servers frontpage extensions | |
serendipity_plugin_api::hook_event('backend_media_path_exclude_directories', $aExclude); | |
$paths = array(); | |
$aFilesOnDisk = array(); | |
$aResultSet = serendipity_traversePath( | |
$serendipity['serendipityPath'] . $serendipity['uploadPath']. $limit_path, | |
'', | |
false, | |
NULL, | |
1, | |
NULL, | |
FALSE, | |
$aExclude | |
); | |
foreach ($aResultSet AS $sKey => $sFile) { | |
if ($sFile['directory']) { | |
if ($debug) echo "<span class='block_level'>{$sFile['relpath']} is a directory.</span>"; | |
array_push($paths, $sFile); | |
} else { | |
if ($debug) echo "<span class='block_level'>{$sFile['relpath']} is a file.</span>"; | |
// Store the file in our array, remove any ending slashes | |
$aFilesOnDisk[$sFile['relpath']] = 1; | |
} | |
unset($aResultSet[$sKey]); | |
} | |
usort($paths, 'serendipity_sortPath'); | |
if ($debug) echo "<p>Got files: <pre>" . print_r($aFilesOnDisk, true) . "</pre></p>"; | |
$serendipity['current_image_hash'] = md5(serialize($aFilesOnDisk)); | |
$nTimeStart = microtime_float(); | |
// MTG 21/01/06: request all images from the database, delete any which don't exist | |
// on the filesystem, and mark off files from the file list which are already | |
// in the database | |
$nCount = 0; | |
if ($debug) { | |
echo "<p>Image Sync Right: " . serendipity_checkPermission('adminImagesSync') . " Onthefly Sync: " . $serendipity['onTheFlySynch'] . " Hash: " . $serendipity['current_image_hash'] . "!=" . $serendipity['last_image_hash']. "</p>"; | |
} | |
if ($serendipity['onTheFlySynch'] && serendipity_checkPermission('adminImagesSync') && ($debug || ($serendipity['current_image_hash'] != $serendipity['last_image_hash']))) { | |
$aResultSet = serendipity_db_query("SELECT path, name, extension, thumbnail_name, id | |
FROM {$serendipity['dbPrefix']}images", false, 'assoc'); | |
if ($debug) { | |
echo "<p>Got images: <pre>" . print_r($aResultSet, true) . "</pre></p>"; | |
} | |
if (is_array($aResultSet)) { | |
foreach ($aResultSet AS $sKey => $sFile) { | |
serendipity_plugin_api::hook_event('backend_thumbnail_filename_select', $sFile); | |
$sThumbNailFile = ''; | |
if (isset($sFile['thumbnail_filename'])) { | |
$sThumbNailFile = $sFile['thumbnail_filename']; | |
} else { | |
$sThumbNailFile = $sFile['path'] . $sFile['name'] . '.' . $sFile['thumbnail_name'] . (empty($sFile['extension']) ? '' : '.' . $sFile['extension']); | |
} | |
$sFileName = $sFile['path'] . $sFile['name'] . (empty($sFile['extension']) ? '' : '.' . $sFile['extension']); | |
if ($debug) { | |
echo "<p>File name is $sFileName, thumbnail is $sThumbNailFile</p>"; | |
} | |
unset($aResultSet[$sKey]); | |
if (isset($aFilesOnDisk[$sFileName])) { | |
unset($aFilesOnDisk[$sFileName]); | |
} else { | |
if ($debug) { | |
echo "<span class='block_level'>Deleting Image {$sFile['id']}</span>"; | |
} | |
serendipity_deleteImage($sFile['id']); | |
++$nCount; | |
} | |
unset($aFilesOnDisk[$sThumbNailFile]); | |
} | |
} | |
if ($nCount > 0){ | |
if ($debug) { | |
echo "<p>Cleaned up ".$nCount." database entries</p>"; | |
} | |
} | |
serendipity_set_config_var('last_image_hash', $serendipity['current_image_hash'], 0); | |
$aUnmatchedOnDisk = array_keys($aFilesOnDisk); | |
if ($debug) { | |
echo "<p>Got unmatched files: <pre>" . print_r($aUnmatchedOnDisk, true) . "</pre></p>"; | |
} | |
$nCount = 0; | |
foreach ($aUnmatchedOnDisk AS $sFile) { | |
if (preg_match('@\.' . $serendipity['thumbSuffix'] . '\.@', $sFile)) { | |
if ($debug) { | |
echo "<p>Skipping thumbnailed file $sFile</p>"; | |
} | |
continue; | |
} else { | |
if ($debug) { | |
echo "<p>Checking $sFile</p>"; | |
} | |
} | |
// MTG: 21/01/06: put files which have just 'turned up' into the database | |
$aImageData = serendipity_getImageData($sFile); | |
if (serendipity_isImage($aImageData, false, '(image)|(video)|(audio)/')) { | |
$nPos = strrpos($sFile, "/"); | |
if (is_bool($nPos) && !$nPos) { | |
$sFileName = $sFile; | |
$sDirectory = ""; | |
} else { | |
++$nPos; | |
$sFileName = substr($sFile, $nPos); | |
$sDirectory = substr($sFile, 0, $nPos); | |
} | |
if ($debug) { | |
echo "<p>Inserting image $sFileName from $sDirectory <pre>" . print_r($aImageData, true) . "</pre> into database</p>"; | |
} | |
# TODO: Check if the thumbnail generation goes fine with Marty's code | |
serendipity_makeThumbnail($sFileName, $sDirectory); | |
serendipity_insertImageInDatabase($sFileName, $sDirectory); | |
++$nCount; | |
} | |
} | |
if ($nCount > 0) { | |
if ($debug) { | |
echo "<p>Inserted ".$nCount." images into the database</p>"; | |
} | |
} | |
} else { | |
if ($debug) { | |
echo "<p>Media Gallery database is up to date</p>"; | |
} | |
} | |
/* | |
$nTimeEnd = microtime_float ( ); | |
$nDifference = $nTimeEnd - $nTimeStart; | |
echo "<p> total time taken was " . $nDifference . "</p>"; | |
*/ | |
## SYNCH FINISHED ## | |
} | |
## Aply ACL afterwards: | |
serendipity_directoryACL($paths, 'read'); | |
// set remember filter settings for SetCookie | |
if (!isset($serendipity['GET']['filter'])) { | |
serendipity_restoreVar($serendipity['COOKIE']['filter'], $serendipity['GET']['filter']); | |
} | |
$serendipity['imageList'] = serendipity_fetchImagesFromDatabase( | |
$start, | |
$perPage, | |
$totalImages, // Passed by ref | |
(isset($serendipity['GET']['sortorder']['order']) ? $serendipity['GET']['sortorder']['order'] : false), | |
(isset($serendipity['GET']['sortorder']['ordermode']) ? $serendipity['GET']['sortorder']['ordermode'] : false), | |
(isset($serendipity['GET']['only_path']) ? $serendipity['GET']['only_path'] : ''), | |
(isset($serendipity['GET']['only_filename']) ? $serendipity['GET']['only_filename'] : ''), | |
(isset($serendipity['GET']['keywords']) ? $serendipity['GET']['keywords'] : ''), | |
(isset($serendipity['GET']['filter']) ? $serendipity['GET']['filter'] : ''), | |
isset($serendipity['GET']['hideSubdirFiles']) | |
); | |
$pages = ceil($totalImages / $perPage); | |
$linkPrevious = '?' . $extraParems . '&serendipity[page]=' . ($page-1); | |
$linkNext = '?' . $extraParems . '&serendipity[page]=' . ($page+1); | |
// Keep the inner to be build first. Now add first and last. Has to do with adding $param to $extraParems. | |
$linkFirst = '?' . $extraParems . '&serendipity[page]=' . 1; | |
$linkLast = '?' . $extraParems . '&serendipity[page]=' . $pages; | |
if (is_null($lineBreak)) { | |
$lineBreak = floor(750 / ($serendipity['thumbSize'] + 20)); | |
} | |
$dprops = $keywords = array(); | |
if ($serendipity['parseMediaOverview']) { | |
$ids = array(); | |
foreach ($serendipity['imageList'] AS $k => $file) { | |
$ids[] = $file['id']; | |
} | |
$allprops =& serendipity_fetchMediaProperties($ids); | |
} | |
if (count($serendipity['imageList']) > 0) { | |
foreach ($serendipity['imageList'] AS $k => $file) { | |
if (!($serendipity['authorid'] == $file['authorid'] || $file['authorid'] == '0' || serendipity_checkPermission('adminImagesViewOthers'))) { | |
// This is a fail-safe continue. Basically a non-matching file should already be filtered in SQL. | |
continue; | |
} | |
serendipity_prepareMedia($serendipity['imageList'][$k], $url); | |
if ($serendipity['parseMediaOverview']) { | |
$serendipity['imageList'][$k]['props'] =& $allprops[$file['id']]; | |
if (!is_array($serendipity['imageList'][$k]['props']['base_metadata'])) { | |
$serendipity['imageList'][$k]['metadata'] =& serendipity_getMetaData($serendipity['imageList'][$k]['realfile'], $serendipity['imageList'][$k]['header']); | |
} else { | |
$serendipity['imageList'][$k]['metadata'] = $serendipity['imageList'][$k]['props']['base_metadata']; | |
serendipity_plugin_api::hook_event('media_getproperties_cached', $serendipity['imageList'][$k]['metadata'], $serendipity['imageList'][$k]['realfile']); | |
} | |
serendipity_parseMediaProperties($dprops, $keywords, $serendipity['imageList'][$k], $serendipity['imageList'][$k]['props'], 3, false); | |
} | |
} | |
} | |
$smarty_vars = array_merge($smarty_vars, array( | |
'limit_path' => $limit_path, | |
'perPage' => $perPage, | |
'show_upload' => $show_upload, | |
'page' => $page, | |
'pages' => $pages, | |
'linkFirst' => $linkFirst, | |
'linkNext' => $linkNext, | |
'linkPrevious' => $linkPrevious, | |
'linkLast' => $linkLast, | |
'extraParems' => $extraParems, | |
'totalImages' => $totalImages | |
)); | |
return serendipity_showMedia( | |
$serendipity['imageList'], | |
$paths, | |
$url, | |
$manage, | |
$lineBreak, | |
true, | |
$smarty_vars | |
); | |
} // End serendipity_displayImageList() | |
/** | |
* Generate the url-parameters needed when generating the ML to select an image to add to the editor, | |
* to store the relevant options (like which textarea to add it to) | |
* | |
* @param string Url or Form format | |
*/ | |
function serendipity_generateImageSelectorParems($format = 'url') { | |
global $serendipity; | |
$sortParams = array('perpage', 'order', 'ordermode'); | |
$importParams = array('adminModule', 'htmltarget', 'filename_only', 'textarea', 'subpage', 'keywords', 'noBanner', 'noSidebar', 'noFooter', 'showUpload','showMediaToolbar'); | |
$extraParems = ''; | |
$filterParams = $serendipity['GET']['filter'] ?: array(); // ?: elvis operator, see http://en.wikipedia.org/wiki/Elvis_operator and upcoming PHP 7 ?? (isset) operator | |
$standaloneFilterParams = array('only_path', 'only_filename'); | |
$parems = array(); | |
foreach($importParams AS $importParam) { | |
if (isset($serendipity['GET'][$importParam])) { | |
$parems['serendipity[' . $importParam . ']'] = $serendipity['GET'][$importParam]; | |
} | |
} | |
foreach($sortParams AS $sortParam) { | |
serendipity_restoreVar($serendipity['COOKIE']['sortorder_' . $sortParam], $serendipity['GET']['sortorder'][$sortParam]); | |
$parems['serendipity[sortorder]['. $sortParam .']'] = $serendipity['GET']['sortorder'][$sortParam]; | |
} | |
foreach($standaloneFilterParams AS $filterParam) { | |
serendipity_restoreVar($serendipity['COOKIE'][$filterParam], $serendipity['GET'][$filterParam]); | |
if (!empty($serendipity['GET'][$filterParam]) && $serendipity['GET'][$filterParam] != "undefined") { | |
$parems['serendipity[' . $filterParam . ']'] = $serendipity['GET'][$filterParam]; | |
} | |
} | |
foreach($filterParams AS $filterParam => $filterValue) { | |
serendipity_restoreVar($serendipity['COOKIE']['filter'][$filterParam], $serendipity['GET']['filter'][$filterParam]); | |
if (!empty($serendipity['GET']['filter'][$filterParam]) && $serendipity['GET']['filter'][$filterParam] != "undefined") { | |
if (is_array($filterValue)) { | |
foreach($filterValue as $key => $value) { | |
$parems['serendipity[filter][' . $filterParam . '][' . $key . ']'] = $value; | |
} | |
} else { | |
$parems['serendipity[filter][' . $filterParam . ']'] = $filterValue; | |
} | |
} | |
} | |
foreach ($parems as $param => $value) { | |
if ($format == "form") { | |
$extraParems .= '<input type="hidden" name="'. $param .'" value="'. serendipity_specialchars($value) .'">'."\n"; | |
} else { | |
$extraParems .= $param.'='. serendipity_specialchars($value) .'&'; | |
} | |
} | |
return rtrim($extraParems, '&'); | |
} | |
/** | |
* Check if a media item is an image | |
* | |
* @access public | |
* @param array File information | |
* @param boolean Use a strict check that does not list PDFs as an image? | |
* @return boolean True if the file is an image | |
*/ | |
function serendipity_isImage(&$file, $strict = false, $allowed = 'image/') { | |
global $serendipity; | |
$file['displaymime'] = $file['mime']; | |
// Strip HTTP path out of imgsrc | |
$file['location'] = $serendipity['serendipityPath'] . preg_replace('@^(' . preg_quote($serendipity['serendipityHTTPPath']) . ')@i', '', $file['imgsrc']); | |
// File is PDF -> Thumb is PNG | |
// Detect PDF thumbs | |
if ($file['mime'] == 'application/pdf' && file_exists($file['location'] . '.png') && $strict == false) { | |
$file['imgsrc'] .= '.png'; | |
$file['displaymime'] = 'image/png'; | |
} | |
return preg_match('@' . $allowed . '@i', $file['displaymime']); | |
} | |
/** | |
* Recursively delete a directory tree | |
* | |
* @access public | |
* @param string The originating directory | |
* @param string The subdirectory | |
* @param boolean Force deleting an directory even if there are files left in it? | |
* @return true | |
*/ | |
function serendipity_killPath($basedir, $directory = '', $forceDelete = false) { | |
static $serious = true; | |
if ($handle = @opendir($basedir . $directory)) { | |
$filestack = []; | |
while (false !== ($file = @readdir($handle))) { | |
if ($file != '.' && $file != '..') { | |
if (is_dir($basedir . $directory . $file)) { | |
serendipity_killPath($basedir, $directory . $file . '/', $forceDelete); | |
} else { | |
$filestack[$file] = $directory . $file; | |
} | |
} | |
} | |
@closedir($handle); | |
echo '<span class="msg_notice"><span class="icon-info-circled" aria-hidden="true"></span> '; | |
printf(CHECKING_DIRECTORY, $directory); | |
echo "</span>"; | |
// No, we just don't kill files the easy way. We sort them out properly from the database | |
// and preserve files not entered therein. | |
$files = serendipity_fetchImagesFromDatabase(0, 0, $total, false, false, $directory); | |
if (is_array($files)) { | |
echo "<ul class='plainList'>\n"; | |
foreach($files AS $f => $file) { | |
echo "<li>\n"; | |
if ($serious) { | |
echo serendipity_deleteImage($file['id']); | |
} else { | |
echo $file['name'] . (empty($file['extension']) ? '' : '.' . $file['extension']); | |
} | |
echo "</li>\n"; | |
unset($filestack[$file['name'] . (empty($file['extension']) ? '' : '.' . $file['extension'])]); | |
unset($filestack[$file['name'] . (!empty($file['thumbnail_name']) ? '.' . $file['thumbnail_name'] : '') . (empty($file['extension']) ? '' : '.' . $file['extension'])]); | |
} | |
echo "</ul>\n"; | |
} | |
if (count($filestack) > 0) { | |
if ($forceDelete) { | |
echo "<ul class='plainList'>\n"; | |
foreach($filestack AS $f => $file) { | |
if ($serious && @unlink($basedir . $file)) { | |
printf('<li><span class="msg_success"><span class="icon-ok-circled" aria-hidden="true"></span> ' . DELETING_FILE . ' ' . DONE . "</span></li>\n", $file); | |
} else { | |
printf('<li><span class="msg_error"><span class="icon-attention-circled" aria-hidden="true"></span> ' . DELETING_FILE . ' ' . ERROR . "</span></li>\n", $file); | |
} | |
} | |
echo "</ul>\n"; | |
} else { | |
echo '<span class="msg_error"><span class="icon-attention-circled" aria-hidden="true"></span> ' . ERROR_DIRECTORY_NOT_EMPTY . "</span>\n"; | |
echo "<ul>\n"; | |
foreach($filestack AS $f => $file) { | |
echo '<li>' . $file . "</li>\n"; | |
} | |
echo "</ul>\n"; | |
} | |
} | |
if ($serious && !empty($directory) && !preg_match('@^.?/?$@', $directory) && @rmdir($basedir . $directory)) { | |
echo '<span class="msg_success"><span class="icon-ok-circled" aria-hidden="true"></span> '; | |
printf(DIRECTORY_DELETE_SUCCESS, $directory); | |
echo "</span>\n"; | |
} else { | |
echo '<span class="msg_error"><span class="icon-attention-circled" aria-hidden="true"></span> '; | |
printf(DIRECTORY_DELETE_FAILED, $directory); | |
echo "</span>\n"; | |
} | |
} | |
return true; | |
} | |
/** | |
* Recursively walk a directory tree | |
* | |
* | |
* @access public | |
* @param string The core directory | |
* @param string The subdirectory | |
* @param boolean Only return directories instead of files as well? | |
* @param string A regexp patter to include files | |
* @param int Level of nesting (recursive use) | |
* @param int The maximum level of nesting (recursive use) | |
* @param mixed Toggle whether to apply serendipity_directoryACL (false / 'read' / 'write') | |
* @param array An array of directories to skip [passed by plugins, for example] | |
* @return array Array of files/directories | |
*/ | |
function serendipity_traversePath($basedir, $dir='', $onlyDirs = true, $pattern = NULL, $depth = 1, $max_depth = NULL, $apply_ACL = false, $aExcludeDirs = NULL) { | |
global $serendipity; | |
if ($serendipity['useInternalCache']) { | |
$key = md5($basedir . $dir . $onlyDirs . $pattern . $depth . $max_depth . $apply_ACL . $aExcludeDirs . $serendipity['serendipityUser']); | |
$files = serendipity_getCacheItem($key); | |
if ($files && $files !== false) { | |
return unserialize($files); | |
} | |
} | |
if ($aExcludeDirs === null) { | |
// add _vti_cnf to exclude possible added servers frontpage extensions | |
// add ckeditor/kcfinders .thumb dir to exclude, since no hook | |
$aExcludeDirs = array("CVS" => true, ".svn" => true, ".thumbs" => true, "_vti_cnf" => true, ".git" => true); | |
} | |
$odir = serendipity_dirSlash('end', $basedir) . serendipity_dirSlash('end', $dir); | |
$dh = @opendir($odir); | |
if (!$dh) { | |
return array(); | |
} | |
$files = array(); | |
while (($file = @readdir($dh)) !== false) { | |
if ($file != '.' && $file != '..') { | |
$bPatternMatch = (is_null($pattern) || preg_match($pattern, $file)); | |
$sFullPath = $odir . $file; | |
$bIsDir = is_dir($sFullPath); | |
if ($onlyDirs === false || $bIsDir) { | |
if ($bPatternMatch && | |
(!$bIsDir || $aExcludeDirs == null || !isset($aExcludeDirs[$file]))) { | |
$files[] = array( | |
'name' => $file, | |
'depth' => $depth, | |
'relpath' => ltrim(str_replace('\\', '/', serendipity_dirSlash('end', $dir)) . basename($file) . ($bIsDir ? '/' : ''), '/'), | |
'directory' => $bIsDir | |
); | |
} | |
} | |
if ($bIsDir && | |
($max_depth === null || $depth < $max_depth) && | |
($aExcludeDirs == null || !isset($aExcludeDirs[$file]))) { | |
$next_dir = serendipity_dirSlash('end', $dir) . basename($file); | |
$files = array_merge($files, serendipity_traversePath($basedir, $next_dir, $onlyDirs, $pattern, ($depth+1), $max_depth, $apply_ACL, $aExcludeDirs)); | |
} | |
} | |
} | |
@closedir($dh); | |
if ($depth == 1 && $apply_ACL !== FALSE) { | |
serendipity_directoryACL($files, $apply_ACL); | |
} | |
if ($serendipity['useInternalCache']) { | |
$key = md5($basedir . $dir . $onlyDirs . $pattern . $depth . $max_depth . $apply_ACL . $aExcludeDirs . $serendipity['serendipityUser']); | |
serendipity_cacheItem($key, serialize($files)); | |
} | |
return $files; | |
} | |
/** | |
* Custom usort() function that properly sorts a path | |
* | |
* @access public | |
* @param array First array | |
* @param array Second array | |
* @return | |
*/ | |
function serendipity_sortPath($a, $b) { | |
return strcasecmp($a['relpath'], $b['relpath']); | |
} | |
/** | |
* Delete a directory with all its files | |
* | |
* @access public | |
* @param string The directory to delete | |
* @return | |
*/ | |
function serendipity_deletePath($dir) { | |
$d = dir($dir); | |
if ($d) { | |
while ($f = $d->read() ){ | |
if ($f != '.' && $f != '..') { | |
if (is_dir($dir . $f)){ | |
serendipity_deletePath($dir . $f . '/'); | |
rmdir($dir . $f); | |
} | |
if (is_file($dir . $f)) { | |
unlink($dir . $f); | |
} | |
} | |
} | |
$d->close(); | |
} | |
} | |
/** | |
* Transform a filename into a valid upload path | |
* | |
* @access public | |
* @param string The input filename | |
* @param boolean Shall all paths be stripped? | |
* @param boolean Shall a trailing slash be appended? | |
* @return string The valid filename | |
*/ | |
function serendipity_uploadSecure($var, $strip_paths = true, $append_slash = false) { | |
$var = str_replace(' ', '_', $var); | |
$var = preg_replace('@[^0-9a-z\._/-]@i', '', $var); | |
$var = preg_replace('@\.+$@i', '', $var); # remove trailing dots | |
if ($strip_paths) { | |
$var = preg_replace('@(\.+[/\\\\]+)@', '/', $var); | |
} | |
$var = preg_replace('@^(/+)@', '', $var); | |
if ($append_slash) { | |
if (!empty($var) && substr($var, -1, 1) != '/') { | |
$var .= '/'; | |
} | |
} | |
# truncate extensions to 5 chars | |
# 1) (\.[^./\\\]{5}) up to five chars after a dot are captured | |
# 2) expression matches only if followed by anything that is no dot | |
# and has no / or \ at the end | |
# 3) if epxression matches, everything after the capture group is deleted | |
$var = preg_replace('@(\.[^./\\\]{5})[^./\\\]+$@', '$1', $var); | |
return $var; | |
} | |
/** | |
* Get the imagesize for a file | |
* | |
* @access public | |
* @param string The filename of the image | |
* @param string The mimetype of an image (can be autodetected) | |
* @param string The file extension of an image | |
* @return array The width/height of the file | |
*/ | |
function serendipity_getimagesize($file, $ft_mime = '', $suf = '') { | |
if (empty($ft_mime) && !empty($suf)) { | |
$ft_mime = serendipity_guessMime($suf); | |
} | |
if ($ft_mime == 'application/pdf') { | |
$fdim = array(1000,1000,24, '', 'bits'=> 24, 'channels' => '3', 'mime' => 'application/pdf'); | |
} else { | |
$fdim = @getimagesize($file); | |
} | |
if (is_array($fdim)) { | |
if (empty($fdim['mime'])) { | |
$fdim['mime'] = $ft_mime; | |
} | |
if ($fdim['mime'] == 'image/vnd.wap.wbmp' && $ft_mime == 'video/x-quicktime') { | |
// PHP Versions prior to 4.3.9 reported .mov files wrongly as WAP. Fix this and mark the file as 'non-image' with 0x0 dimensions | |
$fdim['mime'] = $ft_mime; | |
} | |
} else { | |
// The file is no image. Return a fake array so that files are inserted (but without a thumb) | |
$fdim = array( | |
0 => 0, | |
1 => 0, | |
'mime' => $ft_mime, | |
'noimage' => true | |
); | |
} | |
return $fdim; | |
} | |
/** | |
* Get the available fields of the media database | |
* | |
* @access public | |
* @return array Array with available, sortable fields | |
*/ | |
function serendipity_getImageFields() { | |
global $serendipity; | |
if ($serendipity['simpleFilters'] !== false) { | |
$x = array( | |
'i.date' => array('desc' => SORT_ORDER_DATE, | |
'type' => 'date' | |
), | |
'i.name' => array('desc' => SORT_ORDER_NAME | |
), | |
); | |
} else { | |
$x = array( | |
'i.date' => array('desc' => SORT_ORDER_DATE, | |
'type' => 'date' | |
), | |
'i.name' => array('desc' => SORT_ORDER_NAME | |
), | |
'i.authorid' => array('desc' => AUTHOR, | |
'type' => 'authors' | |
), | |
'i.extension' => array('desc' => SORT_ORDER_EXTENSION | |
), | |
'i.size' => array('desc' => SORT_ORDER_SIZE, | |
'type' => 'intrange' | |
), | |
'i.dimensions_width' => array('desc' => SORT_ORDER_WIDTH, | |
'type' => 'intrange' | |
), | |
'i.dimensions_height' => array('desc' => SORT_ORDER_HEIGHT, | |
'type' => 'intrange' | |
) | |
); | |
$addProp = explode(';', $serendipity['mediaProperties']); | |
foreach($addProp AS $prop) { | |
$parts = explode(':', $prop); | |
$name = $parts[0]; | |
$x['bp.' . $name] = array('desc' => (defined('MEDIA_PROPERTY_' . $name) ? constant('MEDIA_PROPERTY_' . $name) : serendipity_specialchars($name))); | |
if (preg_match('@date@i', $name)) { | |
$x['bp.' . $name]['type'] = 'date'; | |
} | |
if (preg_match('@length@i', $name)) { | |
$x['bp.' . $name]['type'] = 'intrange'; | |
} | |
if (preg_match('@dpi@i', $name)) { | |
$x['bp.' . $name]['type'] = 'int'; | |
} | |
} | |
} | |
return $x; | |
} | |
/** | |
* Escape a shell argument for imagemagick use | |
* | |
* @access public | |
* @param string Input argument | |
* @return string Output argument | |
*/ | |
function serendipity_escapeshellarg($string) { | |
return escapeshellarg(str_replace('%', '', $string)); | |
} | |
/** | |
* Move file with all thumbnails to given directory and update database and entries | |
* */ | |
function serendipity_moveFileTo($id, $dir) { | |
global $serendipity; | |
$file = serendipity_fetchImageFromDatabase($id); | |
serendipity_renameFile($id, $file['name'], $dir); | |
serendipity_updateImageInDatabase(array('path' => $dir), $id); | |
serendipity_updateImageInEntries($id, $file); | |
return true; | |
} | |
/** | |
* Rename a media directory | |
* | |
* @access public | |
* @param string Old directory name | |
* @param string New directory name | |
*/ | |
function serendipity_renameDir($oldDir, $newDir) { | |
global $serendipity; | |
# Plan: 1. Get id of all images under $oldDir (including those in subdirs) | |
# 2. Move $oldDir to newDir | |
# 3. Update image (path) in database | |
# 3. Update image in entries via serendipity_updateImageInEntries | |
$imgBase = serendipity_dirSlash('end', $serendipity['serendipityPath'] . $serendipity['uploadPath']); | |
$total = null; | |
$images = serendipity_fetchImagesFromDatabase(0, 0, $total, false, false, $oldDir); | |
// Perform ACL renames | |
$dirs = serendipity_db_query("SELECT groupid, artifact_id, artifact_type, artifact_mode, artifact_index | |
FROM {$serendipity['dbPrefix']}access | |
WHERE artifact_type = 'directory' | |
AND artifact_index LIKE '" . serendipity_db_escape_string($oldDir) . "%'", false, 'assoc'); | |
if (is_array($dirs)) { | |
foreach($dirs AS $dir) { | |
$old = $dir['artifact_index']; | |
$new = preg_replace('@^(' . preg_quote($oldDir) . ')@i', $newDir, $old); | |
serendipity_db_query("UPDATE {$serendipity['dbPrefix']}access | |
SET artifact_index = '" . serendipity_db_escape_string($new) . "' | |
WHERE groupid = '" . serendipity_db_escape_string($dir['groupid']) . "' | |
AND artifact_id = '" . serendipity_db_escape_string($dir['artifact_id']) . "' | |
AND artifact_type = '" . serendipity_db_escape_string($dir['artifact_type']) . "' | |
AND artifact_mode = '" . serendipity_db_escape_string($dir['artifact_mode']) . "' | |
AND artifact_index = '" . serendipity_db_escape_string($dir['artifact_index']) . "'"); | |
} | |
} | |
if (! file_exists("${imgBase}${newDir}")) { | |
rename("${imgBase}${oldDir}", "${imgBase}${newDir}"); | |
foreach($images as $image) { | |
serendipity_updateImageInDatabase( | |
array('path' => preg_replace('@' . preg_quote(serendipity_dirSlash('end', $oldDir)) . '@', serendipity_dirSlash('end', $newDir), $image['path'], 1)), // we use preg_replace and not str_replace to be able to limit to exacty one replacement, preventing issues when a path has loops in it | |
$image['id'] | |
); | |
serendipity_updateImageInEntries($image['id'], $image); | |
} | |
serendipity_cleanCache(); | |
return true; | |
} | |
return false; | |
} | |
/** | |
* Rename a file in the ML (upload folder and database). Also edit entries to use the new name, and move the thumbnails | |
* | |
* @param Id of the image to be renamed | |
* @param The new name (without extension) | |
* @path The new path to be prepended to the new name, if the file is also to be moved | |
* */ | |
function serendipity_renameFile($id, $newName, $path = null) { | |
global $serendipity; | |
$file = serendipity_fetchImageFromDatabase($id); | |
if (!is_array($file) || (!serendipity_checkPermission('adminImagesMaintainOthers') && $file['authorid'] != '0' && $file['authorid'] != $serendipity['authorid'])) { | |
return; | |
} | |
$oldName = $file['name']; | |
if (LANG_CHARSET == 'UTF-8') { | |
$newName = utf8_encode($newName); | |
} | |
if ($path === null) { | |
$path = $file['path']; | |
} | |
$newName = serendipity_uploadSecure(serendipity_makeFilename($newName), true); | |
$imgBase = $serendipity['serendipityPath'] . $serendipity['uploadPath']; | |
$newPath = $imgBase . $path . $newName . (empty($file['extension']) ? '' : '.' . $file['extension']); | |
if (serendipity_isActiveFile($newPath)) { | |
return sprintf('<span class="msg_error"><span class="icon-attention-circled" aria-hidden="true"></span> ' . ERROR_FILE_FORBIDDEN . "</span>\n", $newName); | |
} | |
if (file_exists($newPath)) { | |
return sprintf('<span class="msg_error"><span class="icon-attention-circled" aria-hidden="true"></span> ' . ERROR_FILE_EXISTS . "</span>\n", $newName); | |
} | |
if (rename("{$imgBase}{$file['path']}{$file['realname']}", $newPath)) { | |
# if renaming was successfull, rename thumbnails and update | |
# databases and entries | |
serendipity_renameThumbnails($id, "{$path}$newName"); | |
serendipity_updateImageInDatabase(array('name' => $newName, 'realname' => basename($newPath)), $id); | |
serendipity_updateImageInEntries($id, $file); | |
} else { | |
return '<span class="msg_error"><span class="icon-attention-circled" aria-hidden="true"></span> ' . MEDIA_RENAME_FAILED . "</span>\n"; | |
} | |
serendipity_cleanCache(); | |
return TRUE; | |
} | |
/** | |
* Rename thumbnails linked to $id | |
* | |
* */ | |
function serendipity_renameThumbnails($id, $newName) { | |
global $serendipity; | |
$file = serendipity_fetchImageFromDatabase($id); | |
$thumbnails = serendipity_getThumbnails($id); | |
foreach($thumbnails as $thumbnail) { | |
$newThumbnail = str_replace("{$file['path']}{$file['name']}", $newName, $thumbnail); | |
rename($thumbnail, $newThumbnail); | |
$eventData = array( // array in array because the event api expects that | |
array( | |
'type' => 'file', // TODO: Use proper preg_quote | |
'oldDir' => preg_replace('@' . $serendipity['serendipityPath'] . $serendipity['uploadPath'] . '@', '', $thumbnail), | |
'newDir' => preg_replace('@' . $serendipity['serendipityPath'] . $serendipity['uploadPath'] . '@', '', $newThumbnail) | |
) | |
); | |
serendipity_plugin_api::hook_event('backend_media_rename', $eventData); | |
} | |
return true; | |
} | |
/** | |
* Get an array of existing thumbnails linked to the image with the given id | |
* | |
* */ | |
function serendipity_getThumbnails($id) { | |
global $serendipity; | |
$file = serendipity_fetchImageFromDatabase($id); | |
$name = $file['name']; | |
$imagePath = $serendipity['serendipityPath'] . $serendipity['uploadPath'] . $file['path'] . $file['realname']; | |
$thumbnailBase = str_replace($file['extension'], '', $imagePath); | |
$uploadDir = dirname($imagePath); | |
return glob("$thumbnailBase*{$file['thumbnail_name']}.{$file['extension']}"); | |
} | |
/** | |
* Set image references to current path in all articles linking to them via the ML | |
* | |
* @id is the id of the image, used for identification | |
* @old is the old file property array of the image, used to keep a manual set link | |
* */ | |
function serendipity_updateImageInEntries($id, $old) { | |
global $serendipity; | |
$file = serendipity_fetchImageFromDatabase($id); | |
$imageHTTPPath = $serendipity['serendipityHTTPPath'] . $serendipity['uploadHTTPPath'] . $file['path'] . $file['realname']; | |
$oldImageHTTPPath = $serendipity['serendipityHTTPPath'] . $serendipity['uploadHTTPPath'] . $old['path'] . $old['realname']; | |
$thumbnailHTTPPath = str_replace(".{$file['extension']}", ".{$file['thumbnail_name']}.{$file['extension']}", $imageHTTPPath); | |
$thumbSuffix = $serendipity['thumbSuffix']; | |
$q = "SELECT id, body, extended FROM {$serendipity['dbPrefix']}entries | |
WHERE | |
body LIKE '%<!-- s9ymdb:$id -->%' OR extended LIKE '%<!-- s9ymdb:$id -->%'"; | |
$entries = serendipity_db_query($q, false, 'assoc'); | |
if (is_array($entries) && !empty($entries)) { | |
foreach($entries as $entry) { | |
# First change the img element, be it a thumbnail or an image | |
$pattern = "@(<!-- s9ymdb:$id -->[^>]*) src=[\"']([^'\"]+)[\"']@"; | |
$callback = function($matches) use ($imageHTTPPath, $thumbnailHTTPPath, $file, $thumbSuffix) { | |
if (strpos($matches[2], "$thumbSuffix.{$file['extension']}") === false) { | |
// the image showed the full size image | |
return $matches[1] . ' src="' . $imageHTTPPath . '"'; | |
} else { | |
return $matches[1] . ' src="' . $thumbnailHTTPPath . '"'; | |
} | |
}; | |
$entry['body'] = preg_replace_callback($pattern, $callback, $entry['body']); | |
$entry['extended'] = preg_replace_callback($pattern, $callback, $entry['extended']); | |
# But we should not forget to update the a element | |
$pattern = "@href=[\"']([^'\"]+)[\"']([^>]*)>(<!-- s9ymdb:$id -->)@"; | |
$callback = function($matches) use ($imageHTTPPath, $thumbnailHTTPPath, $oldImageHTTPPath) { | |
# We only update the link if it is not a manual link, if it pointed to the old image location | |
if ($matches[1] === $oldImageHTTPPath) { | |
return 'href="' . $imageHTTPPath . '"' . $matches[2] . '>' . $matches[3]; | |
} else { | |
return 'href="' . $matches[1] . '"' . $matches[2] . '>' . $matches[3]; | |
} | |
}; | |
$entry['body'] = preg_replace_callback($pattern, $callback, $entry['body']); | |
$entry['extended'] = preg_replace_callback($pattern, $callback, $entry['extended']); | |
$uq = "UPDATE {$serendipity['dbPrefix']}entries | |
SET | |
body = '" . serendipity_db_escape_string($entry['body']) . "' , | |
extended = '" . serendipity_db_escape_string($entry['extended']) . "' | |
WHERE | |
id = " . serendipity_db_escape_string($entry['id']); | |
serendipity_db_query($uq); | |
} | |
} | |
} | |
/** | |
* Makes sure a directory begins with or ends with a "/" | |
* | |
* @access public | |
* @param string Type of where to append/prepend slash ('end', 'start', 'both') | |
* @param string Directory name | |
* @return string Output argument | |
*/ | |
function serendipity_dirSlash($type, $dir) { | |
if ($dir == '') { | |
return $dir; | |
} | |
if ($type == 'start' || $type == 'both') { | |
if (substr($dir, 0, 1) != '/') { | |
$dir = '/' . $dir; | |
} | |
} | |
if ($type == 'end' || $type == 'both') { | |
if (substr($dir, -1) != '/') { | |
$dir .= '/'; | |
} | |
} | |
return $dir; | |
} | |
/** | |
* Cycle a serendipity_traversePath resultset and apply read/write ACLs. | |
* | |
* @access public | |
* @param array serendipity_traversePath result array | |
* @param string ACL type ('read', 'write') | |
*/ | |
function serendipity_directoryACL(&$paths, $type = 'read') { | |
global $serendipity; | |
static $debug = false; | |
if ($debug) { | |
echo "<span class='block_level'>Applying ACL for mode '$type'.</span>"; | |
} | |
if (!is_array($paths)) { | |
return true; | |
} | |
$startCount = count($paths); | |
if (!isset($serendipity['enableACL']) || $serendipity['enableACL'] == true) { | |
// Check if we are a cool superuser. Bail out if we are. | |
$logged_in = serendipity_userLoggedIn(); | |
if ($logged_in && serendipity_checkPermission('adminImagesMaintainOthers') && serendipity_checkPermission('adminImagesDirectories')) { | |
if (!$debug) { | |
return true; | |
} | |
} | |
// Get list of all ACLs for directories. | |
$q = "SELECT a.artifact_index AS directory, | |
a.groupid | |
FROM {$serendipity['dbPrefix']}access AS a | |
WHERE a.artifact_type = 'directory' | |
AND a.artifact_mode = '" . serendipity_db_escape_string($type) . "'"; | |
$allowed = serendipity_db_query($q); | |
if (!is_array($allowed)) { | |
return true; | |
} | |
// Get a list of all the groups for this user. Pipe it into a usable array. | |
if ($logged_in) { | |
$my_groups =& serendipity_getGroups($serendipity['authorid']); | |
$acl_allowed_groups = array(); | |
foreach($my_groups AS $my_group) { | |
$acl_allowed_groups[$my_group['id']] = true; | |
} | |
} else { | |
// Only the 'ALL AUTHORS' group is valid for non-logged in authors. | |
$acl_allowed_groups = array(0 => true); | |
} | |
// Iterate every ACL and check if we are allowed to use it. Parse that data into a workable array. | |
$acl_allowed = array(); | |
foreach($allowed AS $row) { | |
$acl_allowed[$row['directory']][$row['groupid']] = true; | |
} | |
// Iterate the input path array and check it against ACL. | |
foreach($paths AS $idx => $info) { | |
if (!isset($acl_allowed[$info['relpath']])) { | |
// ACL for directory not set. Assume we are allowed to access. | |
continue; | |
} | |
$granted = false; | |
foreach($acl_allowed[$info['relpath']] AS $groupid => $set) { | |
if ($groupid === 0 || isset($acl_allowed_groups[$groupid])) { | |
// We are allowed to access this element | |
$granted = true; | |
break; | |
} | |
} | |
if ($granted === false) { | |
// We are not allowed to access this element | |
if ($debug) { | |
echo '<span class="msg_error"><span class="icon-attention-circled" aria-hidden="true"></span> ACL for ' . $info['relpath'] . " DENIED.</span>"; | |
} | |
unset($paths[$idx]); | |
} else { | |
if ($debug) { | |
echo '<span class="msg_success"><span class="icon-ok-circled" aria-hidden="true"></span> ACL for ' . $info['relpath'] . " granted.</span>"; | |
} | |
} | |
} | |
if (count($paths) < $startCount) { | |
if ($debug) { | |
echo '<span class="msg_error"><span class="icon-attention-circled" aria-hidden="true"></span> ACL denied all.</span>'; | |
} | |
return false; | |
} | |
} | |
return true; | |
} | |
/** | |
* Build the name of a thumbnail image file. | |
* | |
* @author MTG | |
* @param string Relative Path | |
* @param string File name | |
* @param string File extension | |
* @param string Thumbnail suffix | |
* @return array Thumbnail path | |
* | |
*/ | |
function serendipity_getThumbNailPath($sRelativePath, $sName, $sExtension, $sThumbName) { | |
$aTempArray = array('path' => $sRelativePath, | |
'name' => $sName, | |
'extension' => $sExtension); | |
serendipity_plugin_api::hook_event('backend_thumbnail_filename_select', $aTempArray); | |
if (isset($aTempArray['thumbnail_filename'])) { | |
$sThumbNailPath = $aTempArray['thumbnail_filename']; | |
} else { | |
if ($sExtension) { | |
$sThumbNailPath = $sRelativePath . $sName . (!empty($sThumbName) ? '.' . $sThumbName : '') . '.' . $sExtension; | |
} else { | |
$sThumbNailPath = $sRelativePath . $sName . (!empty($sThumbName) ? '.' . $sThumbName : ''); | |
} | |
} | |
return $sThumbNailPath; | |
} | |
/** | |
* Given a relative path to an image, construct an array containing all | |
* relevant information about that image in the file structure. | |
* | |
* @author MTG | |
* @param string Relative Path | |
* @return array Data about image | |
* | |
*/ | |
function &serendipity_getImageData($sRelativePath) { | |
global $serendipity; | |
// First, peel off the file name from the path | |
$nPos = strrpos($sRelativePath, '/'); | |
if (is_bool($nPos) && !$nPos) { | |
$sFileName = $sRelativePath; | |
$sDirectory = ''; | |
} else { | |
$nLastSlashPos = 1 + $nPos; | |
$sFileName = substr($sRelativePath, $nLastSlashPos); | |
$sDirectory = substr($sRelativePath, 0, $nLastSlashPos); | |
} | |
list($sName, $sExtension) = serendipity_parseFileName($sFileName); | |
$sImagePath = $serendipity['serendipityPath'] . $serendipity['uploadPath'] . $sRelativePath; | |
$aSizeData = @serendipity_getimagesize($sImagePath , '', $sExtension); | |
$nWidth = $aSizeData[0]; | |
$nHeight = $aSizeData[1]; | |
$sMime = $aSizeData['mime']; | |
$nFileSize = @filesize($sImagePath); | |
$array = array( | |
'name' => $sName, | |
'extension' => $sExtension, | |
'mime' => $sMime, | |
'size' => $nFileSize, | |
'dimensions_width' => $nWidth, | |
'dimensions_height' => $nHeight, | |
'path' => $sDirectory, | |
'authorid' => 0, | |
'hotlink' => 0, | |
'id' => $sRelativePath, | |
'realname' => $sFileName | |
); | |
return $array; | |
} | |
/** | |
* Shows the HTML form to add/edit properties of uploaded media items | |
* | |
* @param array Associative array holding an array('image_id', 'target', 'created_thumbnail') that points to the uploaded media | |
* @param int How many keyword checkboxes to display next to each other? | |
* @param boolean Can existing data be modified? | |
* @return string Generated HTML | |
* | |
*/ | |
function serendipity_showPropertyForm(&$new_media, $keywordsPerBlock = 3, $is_edit = true) { | |
global $serendipity; | |
if (!is_array($new_media) || count($new_media) < 1) { | |
return true; | |
} | |
$mirror = array(); | |
serendipity_checkPropertyAccess($new_media, $mirror, 'read'); | |
$editform_hidden = ''; | |
if (isset($GLOBALS['image_selector_addvars']) && is_array($GLOBALS['image_selector_addvars'])) { | |
// These variables may come from serendipity_admin_image_selector.php to show embedded upload form | |
foreach($GLOBALS['image_selector_addvars'] AS $imgsel_key => $imgsel_val) { | |
$editform_hidden .= ' <input type="hidden" name="serendipity[' . serendipity_specialchars($imgsel_key) . ']" value="' . serendipity_specialchars($imgsel_val) . '">' . "\n"; | |
} | |
} | |
$dprops = explode(';', $serendipity['mediaProperties']); | |
$keywords = explode(';', $serendipity['mediaKeywords']); | |
$show = array(); | |
foreach($new_media AS $idx => $media) { | |
$props =& serendipity_fetchMediaProperties($media['image_id']); | |
$show[$idx] =& $media['internal']; | |
$show[$idx]['image_id'] = $media['image_id']; | |
serendipity_prepareMedia($show[$idx]); | |
if (!is_array($props['base_metadata'])) { | |
$show[$idx]['metadata'] =& serendipity_getMetaData($show[$idx]['realfile'], $show[$idx]['header']); | |
} else { | |
$show[$idx]['metadata'] = $props['base_metadata']; | |
serendipity_plugin_api::hook_event('media_getproperties_cached', $show[$idx]['metadata'], $show[$idx]['realfile']); | |
} | |
serendipity_parseMediaProperties($dprops, $keywords, $show[$idx], $props, $keywordsPerBlock, $is_edit); | |
} | |
$smarty_vars = array( | |
'is_edit' => $is_edit, | |
'editform_hidden' => $editform_hidden, | |
'keywordsPerBlock' => $keywordsPerBlock, | |
'keywords' => $keywords, | |
'dprops' => $dprops, | |
'case_add' => is_array($new_media[0]['created_thumbnail']) // created_thumbnail is only set when viewing properties after adding an image | |
); | |
return serendipity_showMedia( | |
$show, | |
$mirror, | |
'', | |
false, | |
1, | |
false, | |
$smarty_vars); | |
} | |
/** | |
* Parse/Convert properties | |
* | |
* @param array Holds the property key array | |
* @param array Holds the keyword key array | |
* @param int Holds the media metadata | |
* @param int Holds the media properties | |
* @param int How many keyword checkboxes to display next to each other? | |
* @param boolean Can existing data be modified? | |
* @return boolean | |
* | |
*/ | |
function serendipity_parseMediaProperties(&$dprops, &$keywords, &$media, &$props, $keywordsPerBlock, $is_edit) { | |
global $serendipity; | |
if (!is_array($dprops)) { | |
$dprops = explode(';', $serendipity['mediaProperties']); | |
} | |
if (!is_array($keywords)) { | |
$keywords = explode(';', $serendipity['mediaKeywords']); | |
} | |
$media['references'] = serendipity_db_query("SELECT link, name | |
FROM {$serendipity['dbPrefix']}references | |
WHERE entry_id = " . $media['id'] . " | |
AND type = 'media' | |
ORDER BY name DESC | |
LIMIT 15", false, 'assoc'); | |
if (!is_array($media['references'])) { | |
$media['references'] = false; | |
} | |
foreach($dprops AS $prop) { | |
$type = 'input'; | |
$parts = explode(':', trim($prop)); | |
if (in_array('MULTI', $parts)) { | |
$type = 'textarea'; | |
} | |
if (preg_match('@(AUDIO|VIDEO|DOCUMENT|IMAGE|ARCHIVE|BINARY)@i', $prop)) { | |
$show_item = false; | |
if ($media['mediatype'] == 'video' && in_array('VIDEO', $parts)) { | |
$show_item = true; | |
} | |
if ($media['mediatype'] == 'audio' && in_array('AUDIO', $parts)) { | |
$show_item = true; | |
} | |
if ($media['mediatype'] == 'image' && in_array('IMAGE', $parts)) { | |
$show_item = true; | |
} | |
if ($media['mediatype'] == 'document' && in_array('DOCUMENT', $parts)) { | |
$show_item = true; | |
} | |
if ($media['mediatype'] == 'archive' && in_array('ARCHIVE', $parts)) { | |
$show_item = true; | |
} | |
if ($media['mediatype'] == 'binary' && in_array('BINARY', $parts)) { | |
$show_item = true; | |
} | |
if (!$show_item) { | |
continue; | |
} | |
} | |
if (!$is_edit) { | |
$type = 'readonly'; | |
} | |
$val = serendipity_mediaTypeCast($parts[0], $props['base_property'][$parts[0]], true); | |
$propkey = serendipity_specialchars($parts[0]) . $idx; | |
$media['base_property'][$propkey] = array( | |