7 * @license http://www.gnu.org/licenses/agpl.html AGPL Version 3
8 * @copyright Copyright (c) 2007-2014 Metaways Infosystems GmbH (http://www.metaways.de)
9 * @author Lars Kneschke <l.kneschke@metaways.de>
13 * HTTP interface to Tine
18 class Tinebase_Frontend_Http extends Tinebase_Frontend_Http_Abstract
20 const REQUEST_TYPE = 'HttpPost';
23 * get json-api service map
27 public static function getServiceMap()
29 $smd = Tinebase_Server_Json::getServiceMap();
31 $smdArray = $smd->toArray();
32 unset($smdArray['methods']);
34 if (! isset($_REQUEST['method']) || $_REQUEST['method'] != 'Tinebase.getServiceMap') {
38 header('Content-type: application/json');
39 echo '_smd = ' . json_encode($smdArray);
45 * used to autodiscover openId servers
49 public function getXRDS()
51 // selfUrl == http://servername/pathtotine20/users/loginname
52 $url = dirname(dirname(Zend_OpenId::selfUrl())) . '/index.php?method=Tinebase.openId';
54 header('Content-type: application/xrds+xml');
56 echo '<?xml version="1.0"?>
57 <xrds:XRDS xmlns="xri://$xrd*($v*2.0)" xmlns:xrds="xri://$xrds">
59 <Service priority="0">
60 <Type>http://specs.openid.net/auth/2.0/signon</Type>
61 <URI>' . $url . '</URI>
63 <Service priority="1">
64 <Type>http://openid.net/signon/1.1</Type>
65 <URI>' . $url . '</URI>
67 <Service priority="2">
68 <Type>http://openid.net/signon/1.0</Type>
69 <URI>' . $url . '</URI>
76 * display user info page
78 * in the future we can display public informations about the user here too
79 * currently it is only used as entry point for openId
81 * @param string $username the username
84 public function userInfoPage($username)
86 // selfUrl == http://servername/pathtotine20/users/loginname
87 $openIdUrl = dirname(dirname(Zend_OpenId::selfUrl())) . '/index.php?method=Tinebase.openId';
89 $view = new Zend_View();
90 $view->setScriptPath('Tinebase/views');
92 $view->openIdUrl = $openIdUrl;
93 $view->username = $username;
95 header('Content-Type: text/html; charset=utf-8');
96 echo $view->render('userInfoPage.php');
100 * handle all kinds of openId requests
104 public function openId()
106 Tinebase_Core::startCoreSession();
107 $server = new Tinebase_OpenId_Provider(
110 new Tinebase_OpenId_Provider_User_Tine20(),
111 new Tinebase_OpenId_Provider_Storage()
113 $server->setOpEndpoint(dirname(Zend_OpenId::selfUrl()) . '/index.php?method=Tinebase.openId');
115 // handle openId login form
116 if (isset($_POST['openid_action']) && $_POST['openid_action'] === 'login') {
117 $server->login($_POST['openid_identifier'], $_POST['password'], $_POST['username']);
118 unset($_GET['openid_action']);
119 $this->_setJsonKeyCookie();
120 Zend_OpenId::redirect(dirname(Zend_OpenId::selfUrl()) . '/index.php', $_GET);
122 // display openId login form
123 } else if (isset($_GET['openid_action']) && $_GET['openid_action'] === 'login') {
124 $view = new Zend_View();
125 $view->setScriptPath('Tinebase/views');
127 $view->openIdIdentity = $_GET['openid_identity'];
128 $view->loginName = $_GET['openid_identity'];
130 header('Content-Type: text/html; charset=utf-8');
131 echo $view->render('openidLogin.php');
133 // handle openId trust form
134 } else if (isset($_POST['openid_action']) && $_POST['openid_action'] === 'trust') {
135 if (isset($_POST['allow'])) {
136 if (isset($_POST['forever'])) {
137 $server->allowSite($server->getSiteRoot($_GET));
139 $server->respondToConsumer($_GET);
140 } else if (isset($_POST['deny'])) {
141 if (isset($_POST['forever'])) {
142 $server->denySite($server->getSiteRoot($_GET));
144 Zend_OpenId::redirect($_GET['openid_return_to'],
145 array('openid.mode'=>'cancel'));
148 // display openId trust form
149 } else if (isset($_GET['openid_action']) && $_GET['openid_action'] === 'trust') {
150 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
151 . " Display openId trust screen");
152 $view = new Zend_View();
153 $view->setScriptPath('Tinebase/views');
155 $view->openIdConsumer = $server->getSiteRoot($_GET);
156 $view->openIdIdentity = $server->getLoggedInUser();
158 header('Content-Type: text/html; charset=utf-8');
159 echo $view->render('openidTrust.php');
161 // handle all other openId requests
163 $result = $server->handle();
165 if (is_string($result)) {
167 } elseif ($result !== true) {
168 header('HTTP/1.0 403 Forbidden');
175 * checks if a user is logged in. If not we redirect to login
177 protected function checkAuth()
180 Tinebase_Core::getUser();
181 } catch (Exception $e) {
182 header('HTTP/1.0 403 Forbidden');
188 * renders the login dialog
190 * @todo perhaps we could add a config option to display the update dialog if it is set
192 public function login()
194 // redirect to REDIRECTURL if set
195 $redirectUrl = Tinebase_Config::getInstance()->get(Tinebase_Config::REDIRECTURL, '');
197 if ($redirectUrl !== '' && Tinebase_Config::getInstance()->get(Tinebase_Config::REDIRECTALWAYS, FALSE)) {
198 header('Location: ' . $redirectUrl);
202 // check if setup/update required
203 $setupController = Setup_Controller::getInstance();
204 $applications = Tinebase_Application::getInstance()->getApplicationsByState(Tinebase_Application::ENABLED);
205 foreach ($applications as $application) {
206 if ($setupController->updateNeeded($application)) {
207 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__
208 . " " . $application->name . ' needs an update');
209 $this->setupRequired();
213 $this->_renderMainScreen();
216 * old code used to display user registration
217 * @todo must be reworked
220 $view = new Zend_View();
221 $view->setScriptPath('Tinebase/views');
223 header('Content-Type: text/html; charset=utf-8');
225 // check if registration is active
226 if(isset(Tinebase_Core::getConfig()->login)) {
227 $registrationConfig = Tinebase_Core::getConfig()->registration;
228 $view->userRegistration = (isset($registrationConfig->active)) ? $registrationConfig->active : '';
230 $view->userRegistration = 0;
233 echo $view->render('jsclient.php');
238 * renders the main screen
242 protected function _renderMainScreen()
244 $view = new Zend_View();
245 $baseDir = dirname(dirname(dirname(__FILE__)));
246 $view->setScriptPath("$baseDir/Tinebase/views");
248 if (TINE20_BUILDTYPE =='DEVELOPMENT') {
249 $jsFilestoInclude = $this->_getFilesToWatch('js');
250 $view->devIncludes = $jsFilestoInclude;
253 $view->registryData = array();
255 $this->_setMainscreenHeaders();
257 echo $view->render('jsclient.php');
261 * set headers for mainscreen
263 protected function _setMainscreenHeaders()
265 if (headers_sent()) {
269 header('Content-Type: text/html; charset=utf-8');
271 // obsoleted by CSP see https://www.w3.org/TR/CSP2/#directive-frame-ancestors
272 //header('X-Frame-Options: SAMEORIGIN');
274 $frameAncestors = implode(' ' ,array_merge(
275 (array) Tinebase_Core::getConfig()->get(Tinebase_Config::ALLOWEDJSONORIGINS, array()),
279 // set Content-Security-Policy header against clickjacking and XSS
280 // @see https://developer.mozilla.org/en/Security/CSP/CSP_policy_directives
281 $scriptSrcs = array("'self'", "'unsafe-eval'", 'https://versioncheck.tine20.net');
282 if (TINE20_BUILDTYPE == 'DEVELOPMENT') {
283 $scriptSrcs[] = Tinebase_Core::getUrl('protocol') . '://' . Tinebase_Core::getUrl('host') . ":10443";
285 $scriptSrc = implode(' ', $scriptSrcs);
286 header("Content-Security-Policy: default-src 'self'");
287 header("Content-Security-Policy: script-src $scriptSrc");
288 header("Content-Security-Policy: frame-ancestors $frameAncestors");
290 // headers for IE 10+11
291 header("X-Content-Security-Policy: default-src 'self'");
292 header("X-Content-Security-Policy: script-src $scriptSrc");
293 header("X-Content-Security-Policy: frame-ancestors $frameAncestors");
295 // set Strict-Transport-Security; used only when served over HTTPS
296 header('Strict-Transport-Security: max-age=16070400');
298 // cache mainscreen for one day in production
299 $maxAge = ! defined('TINE20_BUILDTYPE') || TINE20_BUILDTYPE != 'DEVELOPMENT' ? 86400 : -10000;
300 header('Cache-Control: private, max-age=' . $maxAge);
301 header("Expires: " . gmdate('D, d M Y H:i:s', Tinebase_DateTime::now()->addSecond($maxAge)->getTimestamp()) . " GMT");
302 header_remove('Pragma');
306 * renders the setup/update required dialog
308 public function setupRequired()
310 $view = new Zend_View();
311 $view->setScriptPath('Tinebase/views');
313 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->DEBUG(__CLASS__ . '::' . __METHOD__ . ' (' . __LINE__ .') Update/Setup required!');
315 header('Content-Type: text/html; charset=utf-8');
316 echo $view->render('update.php');
321 * login from HTTP post
323 * redirects the tine main screen if authentication is successful
324 * otherwise redirects back to login url
326 public function loginFromPost($username, $password)
328 Tinebase_Core::startCoreSession();
330 if (!empty($username)) {
332 $success = (Tinebase_Controller::getInstance()->login(
335 Tinebase_Core::get(Tinebase_Core::REQUEST),
342 if ($success === TRUE) {
343 $this->_setJsonKeyCookie();
345 $ccAdapter = Tinebase_Auth_CredentialCache::getInstance()->getCacheAdapter();
346 if (Tinebase_Core::isRegistered(Tinebase_Core::USERCREDENTIALCACHE)) {
347 $ccAdapter->setCache(Tinebase_Core::getUserCredentialCache());
349 Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Something went wrong with the CredentialCache / no CC registered.');
351 $ccAdapter->resetCache();
356 $request = new Sabre\HTTP\Request();
357 $redirectUrl = str_replace('index.php', '', $request->getAbsoluteUri());
359 // authentication failed
360 if ($success !== TRUE) {
362 Tinebase_Session::destroyAndRemoveCookie();
364 // redirect back to loginurl if needed
365 $redirectUrl = Tinebase_Config::getInstance()->get(Tinebase_Config::REDIRECTURL, $redirectUrl);
368 // load the client with GET
369 header('Location: ' . $redirectUrl);
373 * put jsonKey into separate cookie
375 * this is needed if login is not done by the client itself
377 protected function _setJsonKeyCookie()
380 $cookie_params = session_get_cookie_params();
382 // don't issue errors in unit tests
385 Tinebase_Core::get('jsonKey'),
387 $cookie_params['path'],
388 $cookie_params['domain'],
389 $cookie_params['secure']
394 * checks authentication and display Tine 2.0 main screen
396 public function mainScreen()
399 $this->_renderMainScreen();
403 * handle session exception for http requests
405 * we force the client to delete session cookie, but we don't destroy
406 * the session on server side. This way we prevent session DOS from thrid party
408 public function sessionException()
410 Tinebase_Session::expireSessionCookie();
412 <script type='text/javascript'>
413 window.location.href = window.location.href;
418 $html = $this->login();
419 $html = ob_get_clean();
422 <script type='text/javascript'>
423 exception = {code: 401};
424 Ext.onReady(function() {
425 Ext.MessageBox.show({
426 title: _('Authorisation Required'),
427 msg: _('Your session is not valid. You need to login again.'),
429 icon: Ext.MessageBox.WARNING
434 echo preg_replace('/<\/head.*>/', $script . '</head>', $html);
439 * generic http exception occurred
441 public function exception()
444 $this->_renderMainScreen();
445 $html = ob_get_clean();
448 <script type='text/javascript'>
449 exception = {code: 400};
450 Ext.onReady(function() {
451 Ext.MessageBox.show({
452 title: _('Abnormal End'),
453 msg: _('An error occurred, the program ended abnormal.'),
455 icon: Ext.MessageBox.WARNING
460 echo preg_replace('/<\/head.*>/', $script . '</head>', $html);
464 * returns javascript of translations for the currently configured locale
466 * @return string (javascript)
468 public function getJsTranslations()
470 if (! in_array(TINE20_BUILDTYPE, array('DEBUG', 'RELEASE'))) {
471 $locale = Tinebase_Core::get('locale');
472 $translations = Tinebase_Translation::getJsTranslations($locale);
473 header('Content-Type: application/javascript');
477 $this->_deliverChangedFiles('lang');
481 * return javascript files if changed
484 public function getJsFiles()
486 $this->_deliverChangedFiles('js');
490 * check if js files have changed and return all js as one big file or return "HTTP/1.0 304 Not Modified" if files don't have changed
492 * @param string $_fileType
493 * @param array $filesToWatch
495 protected function _deliverChangedFiles($_fileType, $filesToWatch=null)
497 // close session to allow other requests
498 Tinebase_Session::writeClose(true);
500 $filesToWatch = $filesToWatch ? $filesToWatch : $this->_getFilesToWatch($_fileType);
501 if ($_fileType == 'js' && TINE20_BUILDTYPE != 'DEVELOPMENT') {
502 $customJSFiles = Tinebase_Config::getInstance()->get(Tinebase_Config::FAT_CLIENT_CUSTOM_JS);
503 if (! empty($customJSFiles)) {
504 $filesToWatch = array_merge($filesToWatch, (array)$customJSFiles);
508 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__CLASS__ . '::' . __METHOD__
509 . ' (' . __LINE__ .') Got files to watch: ' . print_r($filesToWatch, true));
513 header('Cache-Control: private, max-age=' . $maxAge);
514 header("Expires: " . gmdate('D, d M Y H:i:s', Tinebase_DateTime::now()->addSecond($maxAge)->getTimestamp()) . " GMT");
516 // remove Pragma header from session
517 header_remove('Pragma');
519 $clientETag = isset($_SERVER['If_None_Match'])
520 ? $_SERVER['If_None_Match']
521 : (isset($_SERVER['HTTP_IF_NONE_MATCH']) ? $_SERVER['HTTP_IF_NONE_MATCH'] : '');
523 if (preg_match('/[a-f0-9]{40}/', $clientETag, $matches)) {
524 $clientETag = $matches[0];
527 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__CLASS__ . '::' . __METHOD__
528 . ' (' . __LINE__ .') $clientETag: ' . $clientETag);
530 $serverETag = $this->getAssetHash();
532 if ($clientETag == $serverETag) {
533 header("HTTP/1.0 304 Not Modified");
535 header('Content-Type: application/javascript');
536 header('Etag: "' . $serverETag . '"');
538 // send files to client
539 foreach ($filesToWatch as $file) {
542 if ($_fileType != 'lang') {
543 // adds new server version etag for client version check
544 echo "Tine.clientVersion.filesHash = '$serverETag';";
550 * get map of asset files
554 public static function getAssetsMap($asJson=false)
556 $jsonFile = 'Tinebase/js/webpack-assets-FAT.json';
558 if (TINE20_BUILDTYPE =='DEVELOPMENT') {
559 $devServerURL = Tinebase_Config::getInstance()->get('webpackDevServerURL', 'http://localhost:10443');
560 $jsonFileUri = $devServerURL . '/' . $jsonFile;
561 $json = Tinebase_Helper::getFileOrUriContents($jsonFileUri);
563 Tinebase_Core::getLogger()->ERR(__CLASS__ . '::' . __METHOD__ . ' (' . __LINE__ .') Could not get json file: ' . $jsonFile);
564 throw new Exception('You need to run webpack-dev-server in dev mode! See https://wiki.tine20.org/Developers/Getting_Started/Working_with_GIT#Install_webpack');
567 $json = file_get_contents(dirname(dirname(__DIR__)) . '/' . $jsonFile);
570 return $asJson ? $json : json_decode($json, true);
573 public static function getAssetHash()
575 $installedApps = Tinebase_Application::getInstance()->getApplications();
576 $map = self::getAssetsMap();
577 foreach($map as $asset => $ressources) {
578 if (! $installedApps->filter('name', basename($asset))->count()) {
583 return sha1(json_encode($map) . TINE20_BUILDTYPE);
587 * @param string $_fileType
590 * @throws Tinebase_Exception_InvalidArgument
592 protected function _getFilesToWatch($_fileType)
594 $requiredApplications = array('Tinebase', 'Admin', 'Addressbook');
595 $enabledApplications = Tinebase_Application::getInstance()->getApplicationsByState(Tinebase_Application::ENABLED)->name;
596 $orderedApplications = array_merge($requiredApplications, array_diff($enabledApplications, $requiredApplications));
597 $filesToWatch = array();
598 $fileMap = $this->getAssetsMap();
600 foreach ($orderedApplications as $application) {
603 if (isset($fileMap["{$application}/js/{$application}"])) {
604 $jsFile = $fileMap["{$application}/js/{$application}"]['js'];
609 if (TINE20_BUILDTYPE === 'DEBUG') {
610 $jsFile = preg_replace('/\.js$/', '.debug.js', $jsFile);
613 $filesToWatch[] = $jsFile;
616 $fileName = "{$application}/js/{$application}-lang-" . Tinebase_Core::getLocale() . (TINE20_BUILDTYPE == 'DEBUG' ? '-debug' : null) . '.js';
617 $lang = Tinebase_Core::getLocale();
618 $customPath = Tinebase_Config::getInstance()->translations;
619 $basePath = is_readable("$customPath/$lang/$fileName") ? "$customPath/$lang" : '.';
621 $langFile = "{$basePath}/{$application}/js/{$application}-lang-" . Tinebase_Core::getLocale() . (TINE20_BUILDTYPE == 'DEBUG' ? '-debug' : null) . '.js';
622 $filesToWatch[] = $langFile;
625 throw new Exception('no such fileType');
630 return $filesToWatch;
634 * dev mode custom js delivery
636 public function getCustomJsFiles()
639 $customJSFiles = Tinebase_Config::getInstance()->get(Tinebase_Config::FAT_CLIENT_CUSTOM_JS);
640 if (! empty($customJSFiles)) {
641 $this->_deliverChangedFiles('js', $customJSFiles);
643 } catch (Exception $exception) {
644 Tinebase_Core::getLogger()->WARN(__METHOD__ . '::' . __LINE__ . " can't deliver custom js: \n" . $exception);
650 * return last modified timestamp formated in gmt
652 * @param array $_files
655 protected function _getLastModified(array $_files)
659 foreach ($_files as $file) {
660 $mtime = filemtime($file);
661 if ($mtime > $timeStamp) {
666 return gmdate("D, d M Y H:i:s", $timeStamp) . " GMT";
670 * receives file uploads and stores it in the file_uploads db
672 * @throws Tinebase_Exception_UnexpectedValue
673 * @throws Tinebase_Exception_NotFound
675 public function uploadTempFile()
680 // close session to allow other requests
681 Tinebase_Session::writeClose(true);
683 $tempFile = Tinebase_TempFile::getInstance()->uploadTempFile();
685 die(Zend_Json::encode(array(
686 'status' => 'success',
687 'tempFile' => $tempFile->toArray(),
689 } catch (Tinebase_Exception $exception) {
690 Tinebase_Core::getLogger()->WARN(__METHOD__ . '::' . __LINE__ . " File upload could not be done, due to the following exception: \n" . $exception);
692 if (! headers_sent()) {
693 header("HTTP/1.0 500 Internal Server Error");
695 die(Zend_Json::encode(array(
696 'status' => 'failed',
702 * downloads an image/thumbnail at a given size
704 * @param unknown_type $application
706 * @param string $location
709 * @param int $ratiomode
711 public function getImage($application, $id, $location, $width, $height, $ratiomode)
715 // close session to allow other requests
716 Tinebase_Session::writeClose(true);
719 $ifModifiedSince = null;
721 if (isset($_SERVER['If_None_Match'])) {
722 $clientETag = trim($_SERVER['If_None_Match'], '"');
723 $ifModifiedSince = trim($_SERVER['If_Modified_Since'], '"');
724 } elseif (isset($_SERVER['HTTP_IF_NONE_MATCH']) && isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
725 $clientETag = trim($_SERVER['HTTP_IF_NONE_MATCH'], '"');
726 $ifModifiedSince = trim($_SERVER['HTTP_IF_MODIFIED_SINCE'], '"');
729 $image = Tinebase_Controller::getInstance()->getImage($application, $id, $location);
731 if (is_array($image)) {
734 $serverETag = sha1($image->blob . $width . $height . $ratiomode);
736 // cache for 3600 seconds
738 header('Cache-Control: private, max-age=' . $maxAge);
739 header("Expires: " . gmdate('D, d M Y H:i:s', Tinebase_DateTime::now()->addSecond($maxAge)->getTimestamp()) . " GMT");
741 // overwrite Pragma header from session
742 header("Pragma: cache");
744 // if the cache id is still valid
745 if ($clientETag == $serverETag) {
746 header("Last-Modified: " . $ifModifiedSince);
747 header("HTTP/1.0 304 Not Modified");
748 header('Content-Length: 0');
750 #$cache = Tinebase_Core::getCache();
752 #if ($cache->test($serverETag) === true) {
753 # $image = $cache->load($serverETag);
755 if ($width != -1 && $height != -1) {
756 Tinebase_ImageHelper::resize($image, $width, $height, $ratiomode);
758 # $cache->save($image, $serverETag);
761 header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
762 header('Content-Type: '. $image->mime);
763 header('Etag: "' . $serverETag . '"');
772 * crops a image identified by an imgageURL and returns a new tempFileImage
774 * @param string $imageurl imageURL of the image to be croped
775 * @param int $left left position of crop window
776 * @param int $top top position of crop window
777 * @param int $widht widht of crop window
778 * @param int $height heidht of crop window
779 * @return string imageURL of new temp image
782 public function cropImage($imageurl, $left, $top, $widht, $height)
786 $image = Tinebase_Model_Image::getImageFromImageURL($imageurl);
787 Tinebase_ImageHelper::crop($image, $left, $top, $widht, $height);
792 * download file attachment
794 * @param string $nodeId
795 * @param string $recordId
796 * @param string $modelName
798 public function downloadRecordAttachment($nodeId, $recordId, $modelName)
800 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
801 . ' Downloading attachment of ' . $modelName . ' record with id ' . $recordId);
803 $recordController = Tinebase_Core::getApplicationInstance($modelName);
804 $record = $recordController->get($recordId);
806 $node = Tinebase_FileSystem::getInstance()->get($nodeId);
808 $path = Tinebase_Model_Tree_Node_Path::STREAMWRAPPERPREFIX
809 . Tinebase_FileSystem_RecordAttachments::getInstance()->getRecordAttachmentPath($record)
812 $this->_downloadFileNode($node, $path, /* revision */ null, /* $ignoreAcl */ true);
817 * Download temp file to review
821 public function downloadTempfile($tmpfileId)
823 $tmpFile = Tinebase_TempFile::getInstance()->getTempFile($tmpfileId);
825 // some grids can house tempfiles and filemanager nodes, therefor first try tmpfile and if no tmpfile try filemanager
826 if (!$tmpFile && Tinebase_Application::getInstance()->isInstalled('Filemanager')) {
827 $filemanagerNodeController = Filemanager_Controller_Node::getInstance();
828 $file = $filemanagerNodeController->get($tmpfileId);
830 $filemanagerHttpFrontend = new Filemanager_Frontend_Http();
831 $filemanagerHttpFrontend->downloadFile($file->path, null);
834 $this->_downloadFileNode($tmpFile, $tmpFile->path);
838 public function getPostalXWindow()
840 $view = new Zend_View();
841 $view->setScriptPath('Tinebase/views');
843 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG))
844 Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' getPostalXWindow');
846 $this->_setMainscreenHeaders();
848 echo $view->render('postal.xwindow.php');
855 * @param string $_path
856 * @param string $_appId
857 * @param string $_type
859 * @param string $_revision
860 * @throws Tinebase_Exception_InvalidArgument
861 * @throws Tinebase_Exception_NotFound
863 public function downloadPreview($_path, $_appId, $_type, $_num = 0, $_revision = null)
865 $_revision = $_revision ?: null;
868 $path = ltrim($_path, '/');
870 if (strpos($path, 'records/') === 0) {
871 $pathParts = explode('/', $path);
872 $controller = Tinebase_Core::getApplicationInstance($pathParts[1]);
875 $controller->get($pathParts[2]);
877 //$pathRecord = Tinebase_Model_Tree_Node_Path::createFromPath();
878 $node = Tinebase_FileSystem::getInstance()->stat('/' . $_appId . '/folders/' . $path, $_revision);
880 $pathRecord = Tinebase_Model_Tree_Node_Path::createFromPath('/' . $_appId . '/folders/' . $path);
881 $node = Filemanager_Controller_Node::getInstance()->getFileNode($pathRecord, $_revision);
884 throw new Tinebase_Exception_InvalidArgument('A path is needed to download a preview file.');
887 $this->_downloadPreview($node, $_type, $_num);