diff --git a/dev/App/Abstract.js b/dev/App/Abstract.js index 7fcd9d0eb..a10bab8e2 100644 --- a/dev/App/Abstract.js +++ b/dev/App/Abstract.js @@ -117,6 +117,22 @@ return true; }; + AbstractApp.prototype.googlePreviewSupportedCache = null; + + /** + * @return {boolean} + */ + AbstractApp.prototype.googlePreviewSupported = function () + { + if (null === this.googlePreviewSupportedCache) + { + this.googlePreviewSupportedCache = !!Settings.settingsGet('AllowGoogleSocial') && + !!Settings.settingsGet('AllowGoogleSocialPreview'); + } + + return this.googlePreviewSupportedCache; + }; + /** * @param {string} sTitle */ diff --git a/dev/Common/Links.js b/dev/Common/Links.js index ea3ded9bf..a00d84623 100644 --- a/dev/Common/Links.js +++ b/dev/Common/Links.js @@ -65,6 +65,15 @@ return this.sServer + '/Raw/' + this.sSubQuery + this.sSpecSuffix + '/ViewAsPlain/' + sDownload; }; + /** + * @param {string} sDownload + * @return {string} + */ + Links.prototype.attachmentFramed = function (sDownload) + { + return this.sServer + '/Raw' + this.sSubQuery + this.sSpecSuffix + '/FramedView/' + sDownload; + }; + /** * @return {string} */ diff --git a/dev/Common/Utils.js b/dev/Common/Utils.js index 60d322d51..c2fd987f5 100644 --- a/dev/Common/Utils.js +++ b/dev/Common/Utils.js @@ -1093,10 +1093,19 @@ oData.googleEnable = ko.observable(false); oData.googleEnable.auth = ko.observable(false); oData.googleEnable.drive = ko.observable(false); + oData.googleEnable.preview = ko.observable(false); oData.googleClientID = ko.observable(''); oData.googleClientSecret = ko.observable(''); oData.googleApiKey = ko.observable(''); + oData.googleEnable.requireClientSettings = ko.computed(function () { + return oData.googleEnable() && (oData.googleEnable.auth() || oData.googleEnable.drive()); + }); + + oData.googleEnable.requireApiKey = ko.computed(function () { + return oData.googleEnable() && oData.googleEnable.drive(); + }); + oData.dropboxEnable = ko.observable(false); oData.dropboxApiKey = ko.observable(''); diff --git a/dev/Component/AbstractInput.js b/dev/Component/AbstractInput.js index d2603a067..e1a39ca98 100644 --- a/dev/Component/AbstractInput.js +++ b/dev/Component/AbstractInput.js @@ -27,7 +27,7 @@ this.value = oParams.value || ''; this.size = oParams.size || 0; this.label = oParams.label || ''; - this.enable = oParams.enable || true; + this.enable = Utils.isUnd(oParams.enable) ? true : oParams.enable; this.trigger = oParams.trigger && oParams.trigger.subscribe ? oParams.trigger : null; this.placeholder = oParams.placeholder || ''; diff --git a/dev/Model/Attachment.js b/dev/Model/Attachment.js index eef86af85..012424530 100644 --- a/dev/Model/Attachment.js +++ b/dev/Model/Attachment.js @@ -34,6 +34,7 @@ this.folder = ''; this.uid = ''; this.mimeIndex = ''; + this.framed = false; } _.extend(AttachmentModel.prototype, AbstractModel.prototype); @@ -62,6 +63,7 @@ AttachmentModel.prototype.folder = ''; AttachmentModel.prototype.uid = ''; AttachmentModel.prototype.mimeIndex = ''; + AttachmentModel.prototype.framed = false; /** * @param {AjaxJsonAttachment} oJsonAttachment @@ -83,6 +85,7 @@ this.folder = oJsonAttachment.Folder; this.uid = oJsonAttachment.Uid; this.mimeIndex = oJsonAttachment.MimeIndex; + this.framed = !!oJsonAttachment.Framed; this.friendlySize = Utils.friendlySize(this.estimatedSize); this.cidWithOutTags = this.cid.replace(/^<+/, '').replace(/>+$/, ''); @@ -120,6 +123,15 @@ return Globals.bAllowPdfPreview && 'application/pdf' === this.mimeType; }; + /** + * @return {boolean} + */ + AttachmentModel.prototype.isFramed = function () + { + return this.framed && (Globals.__APP__ && Globals.__APP__.googlePreviewSupported()) && + !this.isPdf() && !this.isText() && !this.isImage(); + }; + /** * @return {string} */ @@ -136,6 +148,14 @@ return Links.attachmentPreview(this.download); }; + /** + * @return {string} + */ + AttachmentModel.prototype.linkFramed = function () + { + return Links.attachmentFramed(this.download); + }; + /** * @return {string} */ diff --git a/dev/Screen/Admin/Settings.js b/dev/Screen/Admin/Settings.js index 3b76641e7..44e114aa6 100644 --- a/dev/Screen/Admin/Settings.js +++ b/dev/Screen/Admin/Settings.js @@ -49,7 +49,7 @@ 'AdminSettingsSecurity', 'Security', 'security'); kn.addSettingsViewModel(require('Settings/Admin/Social'), - 'AdminSettingsSocial', 'Social', 'social'); + 'AdminSettingsSocial', 'Integrations', 'integrations'); kn.addSettingsViewModel(require('Settings/Admin/Plugins'), 'AdminSettingsPlugins', 'Plugins', 'plugins'); diff --git a/dev/Settings/Admin/Social.js b/dev/Settings/Admin/Social.js index 6403aae25..d6815298e 100644 --- a/dev/Settings/Admin/Social.js +++ b/dev/Settings/Admin/Social.js @@ -128,6 +128,12 @@ }); }); + self.googleEnable.preview.subscribe(function (bValue) { + Remote.saveAdminConfig(Utils.emptyFunction, { + 'GoogleEnablePreview': bValue ? '1' : '0' + }); + }); + self.googleClientID.subscribe(function (sValue) { Remote.saveAdminConfig(f5, { 'GoogleClientID': Utils.trim(sValue) diff --git a/dev/Storage/AbstractData.js b/dev/Storage/AbstractData.js index de48c1f36..e2dad1731 100644 --- a/dev/Storage/AbstractData.js +++ b/dev/Storage/AbstractData.js @@ -82,6 +82,7 @@ this.googleEnable(!!Settings.settingsGet('AllowGoogleSocial')); this.googleEnable.auth(!!Settings.settingsGet('AllowGoogleSocialAuth')); this.googleEnable.drive(!!Settings.settingsGet('AllowGoogleSocialDrive')); + this.googleEnable.preview(!!Settings.settingsGet('AllowGoogleSocialPreview')); this.googleClientID(Settings.settingsGet('GoogleClientID')); this.googleClientSecret(Settings.settingsGet('GoogleClientSecret')); this.googleApiKey(Settings.settingsGet('GoogleApiKey')); diff --git a/package.json b/package.json index 94f00b1f3..ca6d915dc 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "RainLoop", "title": "RainLoop Webmail", "version": "1.6.10", - "release": "185", + "release": "190", "description": "Simple, modern & fast web-based email client", "homepage": "http://rainloop.net", "main": "gulpfile.js", diff --git a/rainloop/v/0.0.0/app/src/RainLoop/Account.php b/rainloop/v/0.0.0/app/src/RainLoop/Account.php index d60d79f30..57a351c5c 100644 --- a/rainloop/v/0.0.0/app/src/RainLoop/Account.php +++ b/rainloop/v/0.0.0/app/src/RainLoop/Account.php @@ -115,7 +115,7 @@ class Account { return $this->sProxyAuthPassword; } - + /** * @return string */ @@ -324,7 +324,7 @@ class Account { return $this->Domain()->OutVerifySsl($bGlobalVerify); } - + /** * @return string */ @@ -340,7 +340,8 @@ class Account $this->sParentEmail, // 6 \RainLoop\Utils::GetShortToken(), // 7 $this->sProxyAuthUser, // 8 - $this->sProxyAuthPassword // 9 + $this->sProxyAuthPassword, // 9 + 0 // 10 )); } @@ -354,7 +355,7 @@ class Account public function IncConnectAndLoginHelper($oPlugins, $oMailClient, $oConfig) { $bLogin = false; - + $aImapCredentials = array( 'UseConnect' => true, 'UseAuth' => true, @@ -409,7 +410,7 @@ class Account * @param \RainLoop\Plugins\Manager $oPlugins * @param \MailSo\Smtp\SmtpClient $oSmtpClient * @param \RainLoop\Application $oConfig - * + * * @return bool */ public function OutConnectAndLoginHelper($oPlugins, $oSmtpClient, $oConfig) @@ -433,7 +434,7 @@ class Account $oPlugins->RunHook('filter.smtp-credentials', array($this, &$aSmtpCredentials)); $oPlugins->RunHook('event.smtp-pre-connect', array($this, $aSmtpCredentials['UseConnect'], $aSmtpCredentials)); - + if ($aSmtpCredentials['UseConnect']) { $oSmtpClient->Connect($aSmtpCredentials['Host'], $aSmtpCredentials['Port'], @@ -442,7 +443,7 @@ class Account $oPlugins->RunHook('event.smtp-post-connect', array($this, $aSmtpCredentials['UseConnect'], $aSmtpCredentials)); $oPlugins->RunHook('event.smtp-pre-login', array($this, $aSmtpCredentials['UseAuth'], $aSmtpCredentials)); - + if ($aSmtpCredentials['UseAuth']) { $oSmtpClient->Login($aSmtpCredentials['Login'], $aSmtpCredentials['Password']); diff --git a/rainloop/v/0.0.0/app/src/RainLoop/Actions.php b/rainloop/v/0.0.0/app/src/RainLoop/Actions.php index 23d5644a2..4ea873f46 100644 --- a/rainloop/v/0.0.0/app/src/RainLoop/Actions.php +++ b/rainloop/v/0.0.0/app/src/RainLoop/Actions.php @@ -160,6 +160,22 @@ class Actions return $this->sSpecAuthToken; } + /** + * @return string + */ + public function GetShortLifeSpecAuthToken($iLife = 60) + { + $sToken = $this->getAuthToken(); + $aAccountHash = \RainLoop\Utils::DecodeKeyValues($sToken); + if (!empty($aAccountHash[0]) && 'token' === $aAccountHash[0] && is_array($aAccountHash)) + { + $aAccountHash[10] = \time() + $iLife; + return \RainLoop\Utils::EncodeKeyValues($aAccountHash); + } + + return ''; + } + /** * @return \RainLoop\Application */ @@ -471,7 +487,7 @@ class Actions private function getAuthToken() { $sToken = $this->GetSpecAuthToken(); - return $sToken && '_' === \substr($sToken, 0, 1) ? \substr($sToken, 1) : ''; + return !empty($sToken) && '_' === \substr($sToken, 0, 1) ? \substr($sToken, 1) : ''; } /** @@ -887,13 +903,13 @@ class Actions if (!empty($aAccountHash[0]) && 'token' === $aAccountHash[0] && // simple token validation 8 <= \count($aAccountHash) && // length checking !empty($aAccountHash[7]) && // does short token exist - (!$bValidateShortToken || \RainLoop\Utils::GetShortToken() === $aAccountHash[7]) // check short token if needed + (!$bValidateShortToken || \RainLoop\Utils::GetShortToken() === $aAccountHash[7] || // check short token if needed + (isset($aAccountHash[10]) && 0 < $aAccountHash[10] && \time() < $aAccountHash[10])) ) { $oAccount = $this->LoginProvide($aAccountHash[1], $aAccountHash[2], $aAccountHash[3], empty($aAccountHash[5]) ? '' : $aAccountHash[5], $bThrowExceptionOnFalse); - if ($oAccount instanceof \RainLoop\Account) { if (!empty($aAccountHash[8]) && !empty($aAccountHash[9])) // init proxy user/password @@ -1129,18 +1145,29 @@ class Actions $aResult['AllowGoogleSocial'] = (bool) $oConfig->Get('social', 'google_enable', false); $aResult['AllowGoogleSocialAuth'] = (bool) $oConfig->Get('social', 'google_enable_auth', true); $aResult['AllowGoogleSocialDrive'] = (bool) $oConfig->Get('social', 'google_enable_drive', true); + $aResult['AllowGoogleSocialPreview'] = (bool) $oConfig->Get('social', 'google_enable_preview', true); $aResult['GoogleClientID'] = \trim($oConfig->Get('social', 'google_client_id', '')); $aResult['GoogleApiKey'] = \trim($oConfig->Get('social', 'google_api_key', '')); - if ($aResult['AllowGoogleSocial'] && ( - '' === \trim($oConfig->Get('social', 'google_client_id', '')) || '' === \trim($oConfig->Get('social', 'google_client_secret', '')))) + + if (!$aResult['AllowGoogleSocial'] || ($aResult['AllowGoogleSocial'] && ( + '' === \trim($oConfig->Get('social', 'google_client_id', '')) || '' === \trim($oConfig->Get('social', 'google_client_secret', ''))))) { - $aResult['AllowGoogleSocial'] = false; $aResult['AllowGoogleSocialAuth'] = false; $aResult['AllowGoogleSocialDrive'] = false; $aResult['GoogleClientID'] = ''; $aResult['GoogleApiKey'] = ''; } + + if (!$aResult['AllowGoogleSocial']) + { + $aResult['AllowGoogleSocialPreview'] = false; + } + + if ($aResult['AllowGoogleSocial'] && !$aResult['AllowGoogleSocialAuth'] && !$aResult['AllowGoogleSocialDrive'] && !$aResult['AllowGoogleSocialPreview']) + { + $aResult['AllowGoogleSocial'] = false; + } $aResult['AllowFacebookSocial'] = (bool) $oConfig->Get('social', 'fb_enable', false); if ($aResult['AllowFacebookSocial'] && ( @@ -1197,6 +1224,7 @@ class Actions $aResult['AllowGoogleSocial'] = (bool) $oConfig->Get('social', 'google_enable', false); $aResult['AllowGoogleSocialAuth'] = (bool) $oConfig->Get('social', 'google_enable_auth', true); $aResult['AllowGoogleSocialDrive'] = (bool) $oConfig->Get('social', 'google_enable_drive', true); + $aResult['AllowGoogleSocialPreview'] = (bool) $oConfig->Get('social', 'google_enable_preview', true); $aResult['GoogleClientID'] = (string) $oConfig->Get('social', 'google_client_id', ''); $aResult['GoogleClientSecret'] = (string) $oConfig->Get('social', 'google_client_secret', ''); @@ -2495,6 +2523,7 @@ class Actions $this->setConfigFromParams($oConfig, 'GoogleEnable', 'social', 'google_enable', 'bool'); $this->setConfigFromParams($oConfig, 'GoogleEnableAuth', 'social', 'google_enable_auth', 'bool'); $this->setConfigFromParams($oConfig, 'GoogleEnableDrive', 'social', 'google_enable_drive', 'bool'); + $this->setConfigFromParams($oConfig, 'GoogleEnablePreview', 'social', 'google_enable_preview', 'bool'); $this->setConfigFromParams($oConfig, 'GoogleClientID', 'social', 'google_client_id', 'string'); $this->setConfigFromParams($oConfig, 'GoogleClientSecret', 'social', 'google_client_secret', 'string'); $this->setConfigFromParams($oConfig, 'GoogleApiKey', 'social', 'google_api_key', 'string'); @@ -6438,6 +6467,50 @@ class Actions }, $sFolder, $iUid, true, $sMimeIndex); } + /** + * @return string + */ + public function RawFramedView() + { + $oAccount = $this->getAccountFromToken(false); + if ($oAccount) + { + $sRawKey = (string) $this->GetActionParam('RawKey', ''); + $aParams = $this->GetActionParam('Params', null); + $this->Http()->ServerNoCache(); + + $aData = \RainLoop\Utils::DecodeKeyValues($sRawKey); + if (isset($aParams[0], $aParams[1], $aParams[2]) && + 'Raw' === $aParams[0] && 'FramedView' === $aParams[2] && isset($aData['Framed']) && $aData['Framed'] && $aData['FileName']) + { + if ($this->isFileHasFramedPreview($aData['FileName'])) + { + $sNewSpecAuthToken = $this->GetShortLifeSpecAuthToken(); + if (!empty($sNewSpecAuthToken)) + { + $aParams[1] = '_'.$sNewSpecAuthToken; + $aParams[2] = 'View'; + + \array_shift($aParams); + + $sUrl = $this->Http()->GetFullUrl().'?/Raw/&/s/=/'.implode('/', $aParams); + $sFullUrl = 'http://docs.google.com/viewer?embedded=true&url='.urlencode($sUrl); + + @\header('Content-Type: text/html; charset=utf-8'); + echo ''. + ''. + ''. + ''; + } + } + } + } + + + return true; + + } + /** * @return bool * @@ -6659,6 +6732,17 @@ class Actions return $this->rawSmart(false); } + /** + * @param string $sFileName + * + * @return bool + */ + public function isFileHasFramedPreview($sFileName) + { + $sExt = \MailSo\Base\Utils::GetFileExtension($sFileName); + return \in_array($sExt, array('doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx')); + } + /** * @return bool */ @@ -7709,6 +7793,7 @@ class Actions $mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), array( 'Folder' => $mResponse->Folder(), 'Uid' => (string) $mResponse->Uid(), + 'Framed' => false, 'MimeIndex' => (string) $mResponse->MimeIndex(), 'MimeType' => $mResponse->MimeType(), 'FileName' => \MailSo\Base\Utils::ClearFileName( @@ -7721,6 +7806,8 @@ class Actions ($mFoundedContentLocationUrls && \in_array(\trim($mResponse->ContentLocation()), $mFoundedContentLocationUrls)) )); + $mResult['Framed'] = $this->isFileHasFramedPreview($mResult['FileName']); + $mResult['Download'] = \RainLoop\Utils::EncodeKeyValues(array( 'V' => APP_VERSION, 'Account' => $oAccount ? \md5($oAccount->Hash()) : '', @@ -7728,7 +7815,8 @@ class Actions 'Uid' => $mResult['Uid'], 'MimeIndex' => $mResult['MimeIndex'], 'MimeType' => $mResult['MimeType'], - 'FileName' => $mResult['FileName'] + 'FileName' => $mResult['FileName'], + 'Framed' => $mResult['Framed'] )); } else if ('MailSo\Mail\Folder' === $sClassName) diff --git a/rainloop/v/0.0.0/app/src/RainLoop/Config/Application.php b/rainloop/v/0.0.0/app/src/RainLoop/Config/Application.php index f9729e97c..d8137583f 100644 --- a/rainloop/v/0.0.0/app/src/RainLoop/Config/Application.php +++ b/rainloop/v/0.0.0/app/src/RainLoop/Config/Application.php @@ -191,6 +191,7 @@ Examples: 'google_enable' => array(false, 'Google'), 'google_enable_auth' => array(true), 'google_enable_drive' => array(true), + 'google_enable_preview' => array(true), 'google_client_id' => array(''), 'google_client_secret' => array(''), 'google_api_key' => array(''), diff --git a/rainloop/v/0.0.0/app/src/RainLoop/ServiceActions.php b/rainloop/v/0.0.0/app/src/RainLoop/ServiceActions.php index 6d8a86847..85d73fd34 100644 --- a/rainloop/v/0.0.0/app/src/RainLoop/ServiceActions.php +++ b/rainloop/v/0.0.0/app/src/RainLoop/ServiceActions.php @@ -395,7 +395,8 @@ class ServiceActions { $sRawError = ''; $this->oActions->SetActionParams(array( - 'RawKey' => empty($this->aPaths[3]) ? '' : $this->aPaths[3] + 'RawKey' => empty($this->aPaths[3]) ? '' : $this->aPaths[3], + 'Params' => $this->aPaths ), $sMethodName); if (!\call_user_func(array($this->oActions, $sMethodName))) diff --git a/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsSocial.html b/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsSocial.html index 32a16f6b0..0ff9c6f17 100644 --- a/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsSocial.html +++ b/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsSocial.html @@ -20,11 +20,24 @@
+
@@ -35,9 +48,16 @@ Client ID
- -
+
@@ -45,9 +65,15 @@ Client Secret
- -
+

@@ -62,7 +88,7 @@ value: googleApiKey, trigger: googleTrigger3, size: 5, - enable: googleEnable.drive + enable: googleEnable.requireApiKey } }">
@@ -90,9 +116,15 @@ App ID
- -
+
@@ -100,9 +132,15 @@ App Secret
- -
+
@@ -122,9 +160,15 @@ Consumer Key
- -
+
@@ -132,9 +176,15 @@ Consumer Secret
- -
+
@@ -153,9 +203,15 @@ Api Key
- -
+
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/MailMessageView.html b/rainloop/v/0.0.0/app/templates/Views/User/MailMessageView.html index 3c6ae2625..0525443e8 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/MailMessageView.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/MailMessageView.html @@ -282,6 +282,10 @@ data-bind="visible: isPdf(), attr: {href: linkPreview(), title: fileName}" target="_blank"> + + +