getLastLock(); $logPath = $lock ? $lock['log'] : ($lastLock ? $lastLock['log'] : null); $item = array( 'refreshUrl' => $this->url(), 'exportUrl' => $this->url(array('action' => 'export')), 'lock' => $lock, 'lastLock' => $lastLock, 'log' => $logPath && file_exists($logPath) ? file_get_contents($logPath) : null, ); $this->_formDefaults = array( 'path' => self::getDefaultExportPath(), ); $form = $this->_getNewForm(); $form->setDefaults($this->getFormDefaults()); $this->_renderExportForm($form, $item); return $this; } public static function getDefaultExportPath() { $path = App_Settings_Obj::get('partExportPdfPath'); $path = $path ? $path : constant('WWW_PATH') . '/' . App_Part_ExportObj::PART_EXPORT_DIR; return Qs_Fs::normalize($path); } protected function _getNewForm() { $form = parent::_getNewForm(); $form->getElement('action')->setValue('export'); return $form; } /** * @param Qs_Form $form * @return $this */ protected function _bindFormFields($form) { $form->addElement( 'text', 'path', array( 'label' => 'PDF Export Path', ) ); $validator = new Qs_Validate_ExportDir(); $form->getElement('path')->addValidator($validator); return $this; } protected function _bindFormButtons(Qs_Form $form) { parent::_bindFormButtons($form); $form->getElement('btnSubmit')->setLabel('Export'); $form->removeElement('btnCancel'); return $this; } protected function _renderExportForm(Qs_Form $form, $item = array()) { $item['tpl'] = $this->getTemplate('export-form.tpl'); $item['text'] = $form->render(); $this->_addItem($item); return $this; } protected function _doExport() { $form = $this->_getNewForm(); if ($form->validate()) { $data = $form->getValues(); $data['action'] = 'process'; Qs_Job::run($this->url($data)); sleep(1); Qs_Http::redirect($this->url()); } else { $this->_renderMainForm($form); } } protected function _doExportAjax() { $form = $this->_getNewForm(); $this->_validateAjax($form); } protected function _doProcess() { // export last approved revision $data = Qs_Request::getGet(); $path = Qs_Fs::normalize(Qs_Array::get($data, 'path', self::getDefaultExportPath())); $validator = new Qs_Validate_ExportDir(); if (!$validator->isValid($path)) { $msg = 'Error: ' . implode(";\n", $validator->getMessages()); $this->_log($msg); throw new Exception($msg); } if (false == ($printUrl = Qs_SiteMap::findFirst(null, array('type' => 'Process_'), array('type' => 'print'), 'url'))) { $msg = 'Error: Can not find part export url'; $this->_log($msg); throw new Exception($msg); } if (!Qs_ImageFs::ensureDir($path)) { $msg = 'Can not create part export dir'; $this->_log($msg); throw new Exception($msg); } if (($info = self::getLock())) { $msg = 'Import already running since ' . date('g:i a M j, Y', strftime($info['date'])); $this->_log($msg); throw new Exception($msg); } $this->lock($path); sleep(2); $printUrl .= '?action=print'; set_time_limit(0); ignore_user_abort(false); Qs_Db::getInstance()->getProfiler()->setEnabled(false); $parts = $this->_getDataObj()->getParts4Export(); $count = count($parts); $num = 1; $this->_log("PDF export started for {$count} parts"); $processView = new App_Process_View(); foreach ($parts as $row) { $partId = $row['id']; $partNumber = $row['number']; $this->_log("Rendering part #{$partNumber} ... ({$num} of {$count})"); $url = $printUrl . '&partId=' . $partId; $newName = $partNumber . '.pdf'; $newPath = $path . '/' . $newName; $pdfPageHeader = $processView->preparePdfPageHeader($partId); $pdfPageFooter = ''; // $processView->preparePdfPageFooter(); if (($file = self::renderPdf($url, 'part ' . $partId, $pdfPageHeader, $pdfPageFooter))) { file_exists($newPath) && @chmod($newPath, 0644); if (rename($file, $newPath)) { chmod($newPath, 0644); $this->_log("... done ($newName)"); } else { $this->_log("... fail ($newName)"); $this->_log(App_Part_Export_NotMovedException::create($file, $newPath)->createMessage()); } } else { $this->_log("... fail ($newName)"); } ++$num; sleep(1); if (defined('DEV_PDF_EXPORT_LIMIT') && $num >= DEV_PDF_EXPORT_LIMIT) { break; } } $this->_log('PDF export finished'); $this->unlock(); $this->_closeLog(); exit; } public static function renderPdf($resourceUrl, $title, $pageHeader = null, $pageFooter = null) { return App_Pdf_Renderer::render($resourceUrl, $title, compact('pageHeader', 'pageFooter')); } public static function callPdfRenderer($resourceUrl, $file, array $options) { return App_Pdf_Renderer::callRenderer($resourceUrl, $file, $options); } protected function _log($message) { if (null === $this->_log) { $this->_openLog(); } $prefix = date('Y-m-d H:i:s') . ' => '; $message = (is_scalar($message)) ? (string) $message : print_r($message, true); fwrite($this->_log, $prefix . $message . PHP_EOL); return $this; } protected function _openLog() { if (null === $this->_log) { $filePath = self::getLogFilePath(); $path = dirname($filePath); if (Qs_ImageFs::ensureDir($path) && ($handle = fopen($filePath, 'a'))) { $this->_log = $handle; } else { throw new Exception('Can not open log file: ' . $filePath); } } return $this; } protected function _closeLog() { if ($this->_log) { fclose($this->_log); $this->_log = null; } return $this; } public static function getLogFilePath() { if (null === self::$_logPath) { self::$_logPath = constant('BASE_PATH') . '/tmp/part-export/' . date('Y-m-d H:i:s') . '.txt'; } return self::$_logPath; } protected static function _getLockFile() { return constant('BASE_PATH') . '/tmp/part-export.lock'; } public static function getLock() { $lockFile = self::_getLockFile(); $data = (file_exists($lockFile)) ? file_get_contents($lockFile) : null; $data = ($data) ? unserialize($data) : null; if ($data && isset($data['pid']) && posix_getsid($data['pid'])) { return $data; } return null; } protected static function getLastLock() { $lockFile = self::_getLockFile(); $data = (file_exists($lockFile)) ? file_get_contents($lockFile) : null; $data = ($data) ? unserialize($data) : null; if ($data) { return (isset($data['pid']) && posix_getsid($data['pid'])) ? null : $data; } return null; } public static function lock($path, $logFile = null) { if (($info = self::getLock())) { throw new Exception('Import already running in process: ' . $info['pid'] . ' since: ' . $info['date']); } $data = array( 'pid' => posix_getpid(), 'date' => date('Y-m-d H:i:s'), 'path' => $path, 'log' => (null === $logFile) ? self::getLogFilePath() : $logFile, ); file_put_contents(self::_getLockFile(), serialize($data)); } public static function unlock() { if (($lock = self::getLock())) { $lock['pid'] = null; $lock['finish'] = date('Y-m-d H:i:s'); file_put_contents(self::_getLockFile(), serialize($lock)); } } public static function writeFileLockError($newFile) { $filePath = constant('BASE_PATH') . '/tmp/part-export-error/' . date('Y-m-d H:i:s') . '.txt'; $path = dirname($filePath); if (!Qs_ImageFs::ensureDir($path) || !($fh = fopen($filePath, 'a'))) { return false; } $commands = array( 'whoami', 'ls -l ' . escapeshellarg($newFile) . ' 2>&1', 'lsof ' . escapeshellarg($newFile) . ' 2>&1', 'mount 2>&1', 'netstat -nl 2>&1', 'cat /etc/samba/smb.conf 2>&1', ); foreach ($commands as $idx => $cmd) { try { fwrite($fh, "#$ {$cmd}\n"); if (($ph = popen($cmd, 'r'))) { stream_copy_to_stream($ph, $fh); fwrite($fh, "\n\n#----------\n"); pclose($ph); } else { fwrite($fh, "# error executing command\n"); } } catch (\Exception $e) { fwrite($fh, "# error executing command: " . $e->getMessage() . PHP_EOL); } } fclose($fh); return $filePath; } }