'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'); } }