You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

322 lines
14 KiB

<?php
// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
//
// All Rights Reserved. See copyright.txt for details and a complete list of authors.
// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
// $Id$
//this script may only be included - so its better to die if called directly.
if (strpos($_SERVER['SCRIPT_NAME'], basename(__FILE__)) !== false) {
header('location: index.php');
exit;
}
/**
* Reads and manipulates metadata included within a file
* For separate classes for specific file types are called from here
*/
class FileMetadata
{
public $currname = null; //working file path used to access file, may not be the same as embedded in file metadata
public $content = null; //file content string
public $filesize = 0; //file size in bytes
public $basicraw = null; //for basic file information
public $basicinfo = null; //processed basic file information
public $typemeta = null; //array used to store metadata beyond generic file data
public $error = null; //error messages stored here
public $types = [ //files types handled for extended metadata with values used for class and file name
'image/jpeg' => 'jpeg',
'image/jpg' => 'jpeg',
];
/**
* Get basic and extended metadata included in the file itself
*
* @param string $file path to file or content of file
* @param bool $ispath indicates whether $file is a path (true) or the file contents (false)
* @param bool $extended indicates whether to retrieve extended metadata information
*
* @return \FileMetadata FileMetadata object with properties for the various types of metadata
*/
public function getMetadata($file, $ispath = true, $extended = true)
{
if (empty($file)) {
return false;
//set contents and current name as well as type in some situations
} elseif (! $ispath) {
//when $file is actual file contents rather than a path - create a temporary file name
// needed for some php functions
$temppath = $this->temppathFromContent($file);
$leavelink = false;
if (! $temppath) {
$this->error = 'The file is empty';
} else {
$this->filesize = @filesize($temppath);
;
$this->currname = $temppath;
}
} else {
//when $file is a path
if (is_readable($file)) {
$this->currname = $file;
$this->filesize = @filesize($file);
$temppath = $file;
$leavelink = true;
if ($this->filesize <= 0) {
$this->error = 'The file is empty';
}
//if not readable, see if it's an external file
} elseif (strpos($file, 'http') !== false) {
$filegallib = TikiLib::lib('filegal');
$externalinfo = $filegallib->get_info_from_url($file);
$temppath = $this->temppathFromContent($externalinfo['data']);
$leavelink = false;
if (! $temppath) {
$this->error = 'The file is not readable';
} else {
$this->currname = $file;
//go ahead and get content of external file here since this class is only called for external files
//when getting extended metadata through the img plugin
$this->content = $externalinfo['data'];
$this->filesize = @filesize($file);
//set type here for external files
$this->type = $externalinfo['type'];
}
} else {
$this->error = 'The file is not readable';
$leavelink = true;
}
}
//set basic info
$this->basicraw['size'] = $this->filesize;
if (class_exists('finfo') && is_readable($temppath)) {
$finfo = new finfo(FILEINFO_MIME);
$type_charset = $finfo->file($temppath);
$type_charset = explode(';', $type_charset);
//external file types may already be set at this point
$this->basicraw['type'] = empty($this->type) ? $type_charset[0] : $this->type;
$this->basicraw['charset'] = trim($type_charset[1]);
$finfo = new finfo(FILEINFO_DEVICES);
$this->basicraw['devices'] = $finfo->file($temppath);
}
//process basic info
if (is_array($this->basicraw)) {
require_once(__DIR__ . '/../metadata/datatypes/basicinfo.php');
$basic = new BasicInfo();
$this->basicinfo = $basic->processRawData($this->basicraw);
}
//from this point, additional metadata is obtained from classes specific to the file type in separate php files
//all results for this additional metadata go into the $this->typemeta array
if ($extended && $this->canProcessExtended()) {
//set content property
if ($this->content === null) {
$this->content = $ispath === false ? $file : file_get_contents($file);
}
//used for name of class and the file the class is in
$type = $this->types[$this->basicraw['type']];
//file must be named based on $type
include_once('lib/metadata/filetypes/' . $type . '.php');
//class name is same as file name except first letter is capitalized
$type = ucfirst($type);
$typeObj = new $type($this);
$this->typemeta = $typeObj->getExtendedData($this);
//Set client to null to avoid having clients using metadata to access the file content
$this->content = null;
}
$this->setBestMetadata();
if (! $leavelink) {
unlink($temppath);
}
return $this;
}
/**
* Set the most complete and reconciled metadata array. Called by getMetadata.
*/
private function setBestMetadata()
{
if (! empty($this->typemeta['reconciled'])) {
$this->typemeta['best'] = $this->typemeta['reconciled'];
} elseif (! empty($this->typemeta['combined'])) {
$this->typemeta['best'] = $this->typemeta['combined'];
} elseif (! empty($this->basicinfo)) {
$this->typemeta['best'] = ['basiconly' => true, 'Basic Information' =>
['File Data' => $this->basicinfo]];
} else {
$this->typemeta['best'] = [];
}
}
/**
* Merge basic file information into the reconciled or combined metadata array. Also adds data extraction time
*
* @param object $metaObj a FileMetadata object that has had metadata extracted and reconciled
* @param array $metarray the metadata array that is being built from the object
*
* @return array $metarray metarray with merged basic file data and extraction time
*/
public function mergeBasicInfo($metaObj, $metarray)
{
$sumtab = 'Summary of Basic Information';
$timeheader = 'Metadata Extraction Time';
$bheader = 'File Data';
//set time of data extraction as now
global $tikilib, $user;
$extracttime = $tikilib->get_long_datetime(null, $user);
$extractarray = [
$timeheader => [
'Extraction Time' => [
'label' => '',
'newval' => $extracttime,
]
]
];
if (isset($metaObj->basicinfo) && $metaObj->basicinfo !== false) {
if (isset($metarray['reconciled']) && $metarray['reconciled'] !== false) {
//if summary tab is already set
if (isset($metarray['reconciled'][$sumtab][$bheader])) {
//merge in basic info to file data section
array_merge($metarray['reconciled'][$sumtab][$bheader], $metaObj->basicinfo);
} else {
$metarray['reconciled'] =
[$sumtab => [$bheader => $metaObj->basicinfo]] + $metarray['reconciled'];
$metarray['reconciled'][$sumtab][$bheader] = $metaObj->basicinfo;
}
//add extraction time
$metarray['reconciled'][$sumtab] = $extractarray + $metarray['reconciled'][$sumtab];
}
if (isset($metarray['combined']) && is_array($metarray['combined'])) {
$metarray['combined'] = [$sumtab => [$bheader => $metaObj->basicinfo]] + $metarray['combined'];
} else {
$metarray['combined'][$sumtab][$bheader] = $metaObj->basicinfo;
}
$metarray['combined'][$sumtab] = $extractarray + $metarray['combined'][$sumtab];
}
return $metarray;
}
/**
* Used to create a temporary path to a file when only the contents are available
* Necessary because some php functions used to extract metadata require a file path
* @param string $content contents of a file
*
* @return bool|string $temppath path to a temporary file in the temp directory or false if $content is
* empty or file is not writeable
*/
private function temppathFromContent($content)
{
if (! empty($content)) {
$cwd = getcwd();
$temppath = tempnam("$cwd/temp", 'temp_file_');
if (! is_writeable($temppath)) {
return false;
}
$temphandle = fopen($temppath, 'w');
fwrite($temphandle, $content);
fclose($temphandle);
return $temppath;
} else {
return false;
}
}
/**
* Checks to see if Tiki handles the processing of extended metadata for this file type
*
* @return bool
*/
public function canProcessExtended()
{
if (isset($this->basicraw['type']) && array_key_exists($this->basicraw['type'], $this->types)) {
return true;
} else {
return false;
}
}
/**
* To get a segment of binary content based on segment information
*
* @param $binarycontent the binary content that the segment will be extracted from
* @param $marker the marker denoting the beginning of the segment data
* @param $markerlength length of marker, after which the segment size is assumed to be indicated
* @param $sizelength length of the size indicator. actual content assumed to start after marker,
* and size indicator
*
* @return bool|string segment portion is returned
*/
public function getDataSegment($binarycontent, $marker, $markerlength, $sizelength)
{
//find position of segment marker
$markerpos = strpos($binarycontent, $marker);
if ($markerpos === false) {
return false;
} else {
//get the binary value of the size indicator
$rawsize = substr($binarycontent, $markerpos + $markerlength, $sizelength);
//convert the binary string into the size number
$size = unpack('nsize', $rawsize);
//extract the desired segment of data
$segdata = substr($binarycontent, $markerpos + $markerlength + $sizelength, $size['size']);
return $segdata;
}
}
/**
* Creates a Jquery tabbed dialog window for metadata. Assumes a 3-level array: first level is type of data,
* second represents categories or groupings for that type, and third is the fields
*
* @param FileMetadata object $metadata Object or array with necessary properties to be displayed set
* @param string $id HTML id attribute to identify the table
* @param string $id_link HTML id attribute to identify the dialog table link
* @param string $filename Used in the title of the dialog box
* @param bool $mwg_compliant Whether to reconcile extended metadata according to
* the Metadata Working Group guidelines
*
* Calls a smarty template to render the dialog box. The template will require a newval value for each field and
* will check for label and suffix values
*/
public function dialogTabs($metadata, $id, $id_link, $filename)
{
$smarty = TikiLib::lib('smarty');
$smarty->assign('id', $id);
$smarty->assign('id_link', $id_link);
$smarty->assign('filename', $filename);
if (is_array($metadata) && count($metadata) > 0) {
$metarray = $metadata;
} elseif (! empty($metadata)) {
$metarray = json_decode($metadata, true);
}
if (is_array($metarray) && count($metarray) > 0) {
$smarty->assign('metarray', $metarray);
$smarty->assign('type', 'data');
} else {
$smarty->assign('type', 'nodata');
}
return $smarty->fetch('metadata/meta_view_dialog.tpl');
}
public function pageTabs($metadata)
{
$smarty = TikiLib::lib('smarty');
if (is_array($metadata) && count($metadata) > 0) {
$metarray = $metadata;
} elseif (! empty($metadata)) {
$metarray = json_decode($metadata, true);
}
if (is_array($metarray) && count($metarray) > 0) {
$smarty->assign('metarray', $metarray);
$smarty->assign('type', 'data');
} else {
$smarty->assign('type', 'nodata');
}
$smarty->assign('extended', $this->canProcessExtended() ? 'y' : 'n');
$smarty->display('metadata/meta_view_tabs.tpl');
}
}