/** * Compile PDF/image preview for any kind of file * * * @return mixed array or false */ protected function _compile() { // Combine file and folder data $items = $this->_sortIncoming(); // Get stored remote connections $remotes = $this->_getRemoteConnections(); // Params for repo call $params = array('subdir' => $this->subdir, 'remoteConnections' => $remotes); // Incoming $commit = Request::getInt('commit', 0); $download = Request::getInt('download', 0); // Check that we have compile enabled if (!$this->params->get('latex')) { $this->setError(Lang::txt('PLG_PROJECTS_FILES_COMPILE_NOTALLOWED')); return; } // Output HTML $view = new \Hubzero\Plugin\View(array('folder' => 'projects', 'element' => 'files', 'name' => 'compiled')); // Get selected item if (!$items) { $view->setError(Lang::txt('PLG_PROJECTS_FILES_ERROR_NO_FILES_TO_COMPILE')); $view->loadTemplate(); return; } else { foreach ($items as $element) { foreach ($element as $type => $item) { // Get our metadata $file = $this->repo->getMetadata($item, 'file', $params); break; } } } // We need a file if (empty($file)) { $view->setError(Lang::txt('PLG_PROJECTS_FILES_ERROR_NO_FILES_TO_COMPILE')); $view->loadTemplate(); return; } // Path for storing temp previews $imagePath = trim($this->model->config()->get('imagepath', '/site/projects'), DS); $outputDir = DS . $imagePath . DS . strtolower($this->model->get('alias')) . DS . 'compiled'; // Make sure output dir exists if (!is_dir(PATH_APP . $outputDir)) { if (!Filesystem::makeDirectory(PATH_APP . $outputDir)) { $this->setError(Lang::txt('PLG_PROJECTS_FILES_UNABLE_TO_CREATE_UPLOAD_PATH')); return; } } // Get LaTeX helper $compiler = new \Components\Projects\Helpers\Compiler(); // Tex compiler path $texPath = DS . trim($this->params->get('texpath'), DS); $view->file = $file; $view->oWidth = '780'; $view->oHeight = '460'; $view->url = Route::url($this->model->link('files')); $cExt = 'pdf'; // Take out Google native extension if present $fileName = $file->get('name'); if (in_array($file->get('ext'), \Components\Projects\Helpers\Google::getGoogleNativeExts())) { $fileName = preg_replace("/." . $file->get('ext') . "\\z/", "", $file->get('name')); } // Tex file? $tex = $compiler->isTexFile($fileName); // Build temp name $tempBase = $tex ? 'temp__' . \Components\Projects\Helpers\Html::takeOutExt($fileName) : $fileName; $tempBase = str_replace(' ', '_', $tempBase); // Get file contents if (!empty($this->_remoteService) && $file->get('converted')) { // Load remote resource $this->_connect->setUser($this->model->get('owned_by_user')); $resource = $this->_connect->loadRemoteResource($this->_remoteService, $this->model->get('owned_by_user'), $file->get('remoteId')); $cExt = $tex ? 'tex' : \Components\Projects\Helpers\Google::getGoogleImportExt($resource['mimeType']); $cExt = in_array($cExt, array('tex', 'jpeg')) ? $cExt : 'pdf'; $url = \Components\Projects\Helpers\Google::getDownloadUrl($resource, $cExt); // Get data $view->data = $this->_connect->sendHttpRequest($this->_remoteService, $this->model->get('owned_by_user'), $url); } elseif ($file->exists()) { $view->data = $file->isImage() ? NULL : $file->contents(); } else { $this->setError(Lang::txt('PLG_PROJECTS_FILES_ERROR_COMPILE_NO_DATA')); } // LaTeX file? if ($tex && !empty($view->data)) { // Clean up data from Windows characters - important! $view->data = preg_replace('/[^(\\x20-\\x7F)\\x0A]*/', '', $view->data); // Compile and get path to PDF $contentFile = $compiler->compileTex($file->get('fullPath'), $view->data, $texPath, PATH_APP . $outputDir, 1, $tempBase); // Read log (to show in case of error) $logFile = $tempBase . '.log'; if (file_exists(PATH_APP . $outputDir . DS . $logFile)) { $view->log = Filesystem::read(PATH_APP . $outputDir . DS . $logFile); } if (!$contentFile) { $this->setError(Lang::txt('PLG_PROJECTS_FILES_ERROR_COMPILE_TEX_FAILED')); } } elseif ($file->get('converted') && !empty($view->data)) { $tempBase = \Components\Projects\Helpers\Google::getImportFilename($file->get('name'), $cExt); // Write content to temp file $this->_connect->fetchFile($view->data, $tempBase, PATH_APP . $outputDir); $contentFile = $tempBase; } elseif (!$this->getError()) { // Make sure we can handle preview of this type of file if ($file->get('ext') == 'pdf' || $file->isImage() || !$file->isBinary()) { Filesystem::copy($file->get('fullPath'), PATH_APP . $outputDir . DS . $tempBase); $contentFile = $tempBase; } } $url = $this->model->link('files'); $url .= $this->repo->isLocal() ? '' : '&repo=' . $this->repo->get('name'); $url .= $this->subdir ? '&subdir=' . urlencode($this->subdir) : ''; // Parse output if (!empty($contentFile) && file_exists(PATH_APP . $outputDir . DS . $contentFile)) { // Get compiled content mimetype $cType = Filesystem::mimetype(PATH_APP . $outputDir . DS . $contentFile); // Is image? if (strpos($cType, 'image/') !== false) { // Fix up object width & height list($width, $height, $type, $attr) = getimagesize(PATH_APP . $outputDir . DS . $contentFile); $xRatio = $view->oWidth / $width; $yRatio = $view->oHeight / $height; if ($xRatio * $height < $view->oHeight) { // Resize the image based on width $view->oHeight = ceil($xRatio * $height); } else { // Resize the image based on height $view->oWidth = ceil($yRatio * $width); } } // Download compiled file? if ($download) { $pdfName = $tex ? str_replace('temp__', '', basename($contentFile)) : basename($contentFile); // Serve up file $server = new \Hubzero\Content\Server(); $server->filename(PATH_APP . $outputDir . DS . $contentFile); $server->disposition('attachment'); $server->acceptranges(false); $server->saveas($pdfName); $result = $server->serve_attachment(PATH_APP . $outputDir . DS . $contentFile, $pdfName, false); if (!$result) { // Should only get here on error App::abort(404, Lang::txt('PLG_PROJECTS_FILES_SERVER_ERROR')); } else { exit; } } // Add compiled PDF to repository? if ($commit && $tex) { $pdfName = str_replace('temp__', '', basename($contentFile)); $where = $this->subdir ? $this->subdir . DS . $pdfName : $pdfName; if (Filesystem::copy(PATH_APP . $outputDir . DS . $contentFile, $this->_path . DS . $where)) { // Checkin into repo $params = array('subdir' => $this->subdir); $params['file'] = $this->repo->getMetadata($pdfName, 'file', $params); $this->repo->call('checkin', $params); if ($this->repo->isLocal()) { $this->model->saveParam('google_sync_queue', 1); } \Notify::message(Lang::txt('PLG_PROJECTS_FILES_SUCCESS_COMPILED'), 'success', 'projects'); // Redirect to file list App::redirect(Route::url($url)); return; } } // Generate preview image for browsers that cannot embed pdf if ($cType == 'application/pdf') { // GS path $gspath = trim($this->params->get('gspath'), DS); if ($gspath && file_exists(DS . $gspath . DS . 'gs')) { $gspath = DS . $gspath . DS; $pdfName = $tex ? str_replace('temp__', '', basename($contentFile)) : basename($contentFile); $pdfPath = PATH_APP . $outputDir . DS . $contentFile; $exportPath = PATH_APP . $outputDir . DS . $tempBase . '%d.jpg'; exec($gspath . "gs -dNOPAUSE -sDEVICE=jpeg -r300 -dFirstPage=1 -dLastPage=1 -sOutputFile={$exportPath} {$pdfPath} 2>&1", $out); if (is_file(PATH_APP . $outputDir . DS . $tempBase . '1.jpg')) { $hi = new \Hubzero\Image\Processor(PATH_APP . $outputDir . DS . $tempBase . '1.jpg'); if (count($hi->getErrors()) == 0) { $hi->resize($view->oWidth, false, false, true); $hi->save(PATH_APP . $outputDir . DS . $tempBase . '1.jpg'); } else { return false; } } if (is_file(PATH_APP . $outputDir . DS . $tempBase . '1.jpg')) { $image = $tempBase . '1.jpg'; } } } } elseif (!$this->getError()) { $this->setError(Lang::txt('PLG_PROJECTS_FILES_ERROR_COMPILE_PREVIEW_FAILED')); } $view->file = $file; $view->outputDir = $outputDir; $view->embed = $contentFile; $view->cType = $cType; $view->subdir = $this->subdir; $view->option = $this->_option; $view->image = !empty($image) ? $image : NULL; $view->model = $this->model; $view->repo = $this->repo; if ($this->getError()) { $view->setError($this->getError()); } return $view->loadTemplate(); }
/** * Compiles PDF/image preview for any kind of file * * @return string */ public function compile() { // Combine file and folder data $items = $this->getCollection(); // Incoming $download = Request::getInt('download', 0); // Check that we have compile enabled // @FIXME: why are latex and compiled preview tied together? // presumedly we are also 'compiling' pdfs? if (!$this->params->get('latex')) { $this->setError(Lang::txt('PLG_PROJECTS_FILES_COMPILE_NOTALLOWED')); return; } // Output HTML $view = new \Hubzero\Plugin\View(['folder' => 'projects', 'element' => 'files', 'name' => 'connected', 'layout' => 'compiled']); // Make sure we have an item if (count($items) == 0) { $view->setError(Lang::txt('PLG_PROJECTS_FILES_ERROR_NO_FILES_TO_COMPILE')); $view->loadTemplate(); return; } // We can only handle one file at a time $file = $items->first(); // Build path for storing temp previews $imagePath = trim($this->model->config()->get('imagepath', '/site/projects'), DS); $outputDir = DS . $imagePath . DS . strtolower($this->model->get('alias')) . DS . 'compiled'; // Make sure output dir exists if (!is_dir(PATH_APP . $outputDir)) { if (!Filesystem::makeDirectory(PATH_APP . $outputDir)) { $this->setError(Lang::txt('PLG_PROJECTS_FILES_UNABLE_TO_CREATE_UPLOAD_PATH')); return; } } // Get LaTeX helper $compiler = new \Components\Projects\Helpers\Compiler(); // Tex compiler path $texPath = DS . trim($this->params->get('texpath'), DS); // Set view args and defaults $view->file = $file; $view->oWidth = '780'; $view->oHeight = '460'; $view->url = $this->model->link('files'); $cExt = 'pdf'; // Tex file? $tex = $compiler->isTexFile($file->getName()); // Build temp name $tempBase = $tex ? 'temp__' . \Components\Projects\Helpers\Html::takeOutExt($file->getName()) : $file->getName(); $tempBase = str_replace(' ', '_', $tempBase); $view->data = $file->isImage() ? NULL : $file->read(); // LaTeX file? if ($tex && !empty($view->data)) { // Clean up data from Windows characters - important! $view->data = preg_replace('/[^(\\x20-\\x7F)\\x0A]*/', '', $view->data); // Store file locally $tmpfile = PATH_APP . $outputDir . DS . $tempBase; file_put_contents($tmpfile, $view->data); // Compile and get path to PDF $contentFile = $compiler->compileTex($tmpfile, $view->data, $texPath, PATH_APP . $outputDir, 1, $tempBase); // Read log (to show in case of error) $logFile = $tempBase . '.log'; if (file_exists(PATH_APP . $outputDir . DS . $logFile)) { $view->log = Filesystem::read(PATH_APP . $outputDir . DS . $logFile); } if (!$contentFile) { $this->setError(Lang::txt('PLG_PROJECTS_FILES_ERROR_COMPILE_TEX_FAILED')); } $cType = Filesystem::mimetype(PATH_APP . $outputDir . DS . $contentFile); } else { // Make sure we can handle preview of this type of file if ($file->hasExtension('pdf') || $file->isImage() || !$file->isBinary()) { $origin = $this->connection->provider->alias . '://' . $file->getPath(); $dest = 'compiled://' . $tempBase; // Do the copy Manager::adapter('local', ['path' => PATH_APP . $outputDir . DS], 'compiled'); Manager::copy($origin, $dest); $contentFile = $tempBase; } } // Parse output if (!empty($contentFile) && file_exists(PATH_APP . $outputDir . DS . $contentFile)) { // Get compiled content mimetype $cType = Filesystem::mimetype(PATH_APP . $outputDir . DS . $contentFile); // Is image? if (strpos($cType, 'image/') !== false) { // Fix up object width & height list($width, $height, $type, $attr) = getimagesize(PATH_APP . $outputDir . DS . $contentFile); $xRatio = $view->oWidth / $width; $yRatio = $view->oHeight / $height; if ($xRatio * $height < $view->oHeight) { // Resize the image based on width $view->oHeight = ceil($xRatio * $height); } else { // Resize the image based on height $view->oWidth = ceil($yRatio * $width); } } // Download compiled file? if ($download) { $pdfName = $tex ? str_replace('temp__', '', basename($contentFile)) : basename($contentFile); // Serve up file $server = new \Hubzero\Content\Server(); $server->filename(PATH_APP . $outputDir . DS . $contentFile); $server->disposition('attachment'); $server->acceptranges(false); $server->saveas($pdfName); $result = $server->serve(); if (!$result) { // Should only get here on error throw new Exception(Lang::txt('PLG_PROJECTS_FILES_SERVER_ERROR'), 404); } else { exit; } } // Generate preview image for browsers that cannot embed pdf if ($cType == 'application/pdf') { // GS path $gspath = trim($this->params->get('gspath'), DS); if ($gspath && file_exists(DS . $gspath . DS . 'gs')) { $gspath = DS . $gspath . DS; $pdfName = $tex ? str_replace('temp__', '', basename($contentFile)) : basename($contentFile); $pdfPath = PATH_APP . $outputDir . DS . $contentFile; $exportPath = PATH_APP . $outputDir . DS . $tempBase . '%d.jpg'; exec($gspath . "gs -dNOPAUSE -sDEVICE=jpeg -r300 -dFirstPage=1 -dLastPage=1 -sOutputFile={$exportPath} {$pdfPath} 2>&1", $out); if (is_file(PATH_APP . $outputDir . DS . $tempBase . '1.jpg')) { $hi = new \Hubzero\Image\Processor(PATH_APP . $outputDir . DS . $tempBase . '1.jpg'); if (count($hi->getErrors()) == 0) { $hi->resize($view->oWidth, false, false, true); $hi->save(PATH_APP . $outputDir . DS . $tempBase . '1.jpg'); } else { return false; } } if (is_file(PATH_APP . $outputDir . DS . $tempBase . '1.jpg')) { $image = $tempBase . '1.jpg'; } } } } elseif (!$this->getError()) { $this->setError(Lang::txt('PLG_PROJECTS_FILES_ERROR_COMPILE_PREVIEW_FAILED')); } $view->file = $file; $view->outputDir = $outputDir; $view->embed = $contentFile; $view->cType = $cType; $view->subdir = $this->subdir; $view->option = $this->_option; $view->image = !empty($image) ? $image : NULL; $view->model = $this->model; $view->repo = $this->repo; $view->connection = $this->connection; if ($this->getError()) { $view->setError($this->getError()); } return $view->loadTemplate(); }
/** * Set mime type * * @return mixed */ public function setMimeType() { if (!$this->get('mimeType')) { $this->set('mimeType', Filesystem::mimetype($this->get('fullPath'))); } }
/** * Get changes for sync * * @return array */ public function getChanges($localPath = '', $synced = '', $localDir = '', &$localRenames, $connections) { // Collector array $locals = array(); // Initial sync if ($synced == 1) { $files = $this->callGit('ls-files --full-name ' . escapeshellarg($localDir)); $files = empty($files) || substr($files[0], 0, 5) == 'fatal' ? array() : $files; if (empty($files)) { return $locals; } foreach ($files as $filename) { $type = 'file'; // We are only interested in last local change on the file if (!isset($locals[$filename])) { $time = strtotime(date('c', time())); // Important! needs to be local time, NOT UTC $locals[$filename] = array('status' => 'A', 'time' => $time, 'type' => $type, 'remoteid' => 0, 'converted' => 0, 'rParent' => NULL, 'local_path' => $filename, 'title' => basename($filename), 'author' => NULL, 'modified' => gmdate('Y-m-d H:i:s', $time), 'synced' => NULL, 'fullPath' => $localPath . DS . $filename, 'mimeType' => Filesystem::mimetype($localPath . DS . $filename), 'md5' => NULL, 'rename' => NULL); } } } else { // Collect $since = $synced != 1 ? ' --since="' . $synced . '"' : ''; $where = $localDir ? ' --all -- ' . escapeshellarg($localDir) . ' ' : ' --all '; $changes = $this->callGit('rev-list ' . $where . $since); // Empty repo or no changes? if (empty($changes) || trim(substr($changes[0], 0, 5)) == 'usage') { $changes = array(); } // Parse Git file list to find which items changed since last sync if (count($changes) > 0) { $timestamps = array(); // Get files involved in each commit foreach ($changes as $hash) { // Get time and author of commit $time = $this->gitLog('', $hash, 'timestamp'); $author = $this->gitLog('', $hash, 'author'); // Get filename and change $fileinfo = $this->callGit('diff --name-status ' . $hash . '^ ' . $hash); // First commit if (!empty($fileinfo) && !empty($fileinfo[0]) && substr($fileinfo[0], 0, 5) == 'fatal') { $fileinfo = $this->callGit('log --pretty=oneline --name-status --root'); if (!empty($fileinfo)) { // Remove first line array_shift($fileinfo); } } if (empty($fileinfo)) { continue; } // Go through files foreach ($fileinfo as $line) { $n = substr($line, 0, 1); if ($n == 'f') { // First file in repository $finfo = $this->callGit('log --pretty=oneline --name-status ' . $hash); $status = 'A'; $filename = trim(substr($finfo[1], 1)); break; } else { $status = $n; $filename = trim(substr($line, 1)); } $type = 'file'; $rename = ''; // Detect a rename if (isset($localRenames[$filename])) { $rename = $localRenames[$filename]; } else { $rename = $this->getRename($filename, $hash, $since); if ($rename && $status == 'A') { // Rename or move? if (basename($rename) == basename($filename)) { $status = 'W'; // this means 'move' } else { $status = 'R'; } $localRenames[$filename] = $rename; } } // Hidden file in local directory - treat as directory if (preg_match("/.gitignore/", $filename)) { $filename = dirname($filename); // Skip home directory if ($filename == '.') { continue; } $type = 'folder'; } // Specific local directory is synced? $lFilename = $localDir ? preg_replace("/^" . $localDir . "\\//", "", $filename) : $filename; $conn = isset($connections['paths']) ? $connections['paths'] : NULL; $search = $status == 'R' || $status == 'W' ? $rename : $filename; $found = isset($conn[$search]) && $conn[$search]['type'] == $type ? $conn[$search] : false; // Rename/move connection not found - check against new name in case of repeat sync if (!$found && ($status == 'R' || $status == 'W')) { $found = isset($conn[$filename]) && $conn[$filename]['type'] == $type ? $conn[$filename] : false; } $remoteid = $found ? $found['remote_id'] : NULL; $converted = $found ? $found['converted'] : 0; $rParent = $found ? $found['rParent'] : NULL; $syncT = $found ? $found['synced'] : NULL; $md5Checksum = $type == 'file' && file_exists($localPath . DS . $filename) ? hash_file('md5', $localPath . DS . $filename) : NULL; $mimeType = $type == 'file' ? Filesystem::mimetype($localPath . DS . $filename) : NULL; // We are only interested in last local change on the file if (!isset($locals[$lFilename])) { $locals[$lFilename] = array('status' => $status, 'time' => $time, 'type' => $type, 'remoteid' => $remoteid, 'converted' => $converted, 'rParent' => $rParent, 'local_path' => $filename, 'title' => basename($filename), 'author' => $author, 'modified' => gmdate('Y-m-d H:i:s', $time), 'synced' => $syncT, 'fullPath' => $localPath . DS . $filename, 'mimeType' => $mimeType, 'md5' => $md5Checksum, 'rename' => $rename); $timestamps[] = $time; } } } // Sort by time, most recent first //array_multisort($timestamps, SORT_DESC, $locals, SORT_STRING); } } return $locals; }