From 6116597f6fe03a34de6d850357d4d81c747e2d7c Mon Sep 17 00:00:00 2001 From: RainLoop Team Date: Thu, 12 Feb 2015 01:39:27 +0400 Subject: [PATCH] Many fixes New ownCloud package with a built-in webmail --- build/owncloud/rainloop-app/INSTALL | 49 ++- build/owncloud/rainloop-app/admin.php | 52 ++-- build/owncloud/rainloop-app/ajax/admin.php | 22 +- build/owncloud/rainloop-app/ajax/personal.php | 4 +- build/owncloud/rainloop-app/app.php | 10 + build/owncloud/rainloop-app/appinfo/app.php | 2 +- build/owncloud/rainloop-app/appinfo/info.xml | 5 +- .../owncloud/rainloop-app/appinfo/routes.php | 13 + build/owncloud/rainloop-app/index.php | 25 +- build/owncloud/rainloop-app/js/admin.js | 26 +- build/owncloud/rainloop-app/js/personal.js | 26 +- .../rainloop-app/lib/RainLoopHelper.php | 112 +++++++ build/owncloud/rainloop-app/personal.php | 16 +- .../rainloop-app/templates/admin-local.php | 29 ++ dev/App/Abstract.js | 33 ++ dev/App/User.js | 11 +- dev/Common/Enums.js | 1 + dev/Common/Events.js | 20 +- dev/Common/Globals.js | 4 +- dev/Common/HtmlEditor.js | 7 +- dev/Common/Utils.js | 1 + dev/Knoin/AbstractModel.js | 2 +- dev/Model/Template.js | 27 ++ dev/Screen/User/Settings.js | 7 +- dev/Settings/Admin/General.js | 7 + dev/Settings/User/Accounts.js | 1 - dev/Storage/User/Remote.js | 12 +- dev/Stores/Admin/Capa.js | 2 + dev/Stores/User/Template.js | 24 +- dev/Styles/SettingsTemplates.less | 2 +- dev/Styles/Template.less | 6 +- dev/View/Popup/Compose.js | 37 +-- dev/View/Popup/Template.js | 99 ++++-- dev/View/User/MailBox/MessageView.js | 4 +- gulpfile.js | 34 +- package.json | 6 +- .../0.0.0/app/libraries/MailSo/Base/Http.php | 1 - .../MailSo/Base/StreamWrappers/SubStreams.php | 4 +- .../0.0.0/app/libraries/MailSo/Base/Utils.php | 22 ++ .../0.0.0/app/libraries/MailSo/Log/Logger.php | 2 +- .../app/libraries/MailSo/Mail/MailClient.php | 4 +- .../app/libraries/MailSo/Mime/Message.php | 5 +- .../0.0.0/app/libraries/RainLoop/Actions.php | 293 ++++++++++++++++-- .../v/0.0.0/app/libraries/RainLoop/Api.php | 2 +- .../libraries/RainLoop/Config/Application.php | 4 +- .../libraries/RainLoop/Enumerations/Capa.php | 1 + .../app/libraries/RainLoop/Model/Template.php | 149 +++++++++ .../Providers/AddressBook/Classes/Contact.php | 2 +- .../libraries/RainLoop/Providers/Files.php | 9 + .../Providers/Files/DefaultStorage.php | 35 +++ .../0.0.0/app/libraries/RainLoop/Service.php | 9 +- .../app/libraries/RainLoop/ServiceActions.php | 56 +++- .../v/0.0.0/app/libraries/RainLoop/Utils.php | 4 +- .../Views/Admin/AdminSettingsGeneral.html | 13 + .../templates/Views/User/PopupsTemplate.html | 12 +- rainloop/v/0.0.0/include.php | 4 +- 56 files changed, 1137 insertions(+), 232 deletions(-) create mode 100644 build/owncloud/rainloop-app/app.php create mode 100644 build/owncloud/rainloop-app/appinfo/routes.php create mode 100644 build/owncloud/rainloop-app/templates/admin-local.php create mode 100644 rainloop/v/0.0.0/app/libraries/RainLoop/Model/Template.php diff --git a/build/owncloud/rainloop-app/INSTALL b/build/owncloud/rainloop-app/INSTALL index 8a8662368..314f5babe 100755 --- a/build/owncloud/rainloop-app/INSTALL +++ b/build/owncloud/rainloop-app/INSTALL @@ -1,26 +1,23 @@ -************************************************************************ -* -* ownCloud - RainLoop Webmail mail plugin -* -* @author RainLoop Team -* @copyright 2014 RainLoop Team -* -* https://github.com/RainLoop/rainloop-webmail/tree/master/build/owncloud -* -************************************************************************ - -REQUIREMENTS: -- Installed and configured RainLoop Webmail (standalone) -- ownCloud version 6 or higher -- Both apps (RainLoop & ownCloud) running on the same domain - - -INSTALL: -- Unpack the RainLoop Webmail application package in the apps directory of your OwnCloud instance - - -CONFIGURATION: -- ownCloud: - 1) In the Apps > Enable 'RainLoop' plugin - 2) In the Settings > Admin > Enter "RainLoop Webmail URL" and "Absolute (full) path to RainLoop Webmail installation" - 3) In the Settings > Personal > Type your mail server email (login) and password +************************************************************************ +* +* ownCloud - RainLoop Webmail package +* +* @author RainLoop Team +* @copyright 2015 RainLoop Team +* +* https://github.com/RainLoop/rainloop-webmail/tree/master/build/owncloud +* +************************************************************************ + +REQUIREMENTS: +- ownCloud version 6 or higher + + +INSTALL: +- Unpack the RainLoop Webmail application package in the apps directory of your OwnCloud instance + + +CONFIGURATION: +- ownCloud: + 1) In the Apps > Enable 'RainLoop' plugin + 2) In the Settings > Personal > Type your mail server email (login) and password diff --git a/build/owncloud/rainloop-app/admin.php b/build/owncloud/rainloop-app/admin.php index ba8913669..3fc6a67bd 100644 --- a/build/owncloud/rainloop-app/admin.php +++ b/build/owncloud/rainloop-app/admin.php @@ -1,20 +1,32 @@ -assign('rainloop-url', OCP\Config::getAppValue('rainloop', 'rainloop-url', '')); -$oTemplate->assign('rainloop-path', OCP\Config::getAppValue('rainloop', 'rainloop-path', '')); -$oTemplate->assign('rainloop-autologin', OCP\Config::getAppValue('rainloop', 'rainloop-autologin', false)); -return $oTemplate->fetchPage(); +assign('rainloop-admin-panel-link', + OC_RainLoop_Helper::getAppUrl().'?admin'); +} +else +{ + $oTemplate = new OCP\Template('rainloop', 'admin'); + $oTemplate->assign('rainloop-admin-panel-link', ''); + $oTemplate->assign('rainloop-url', OCP\Config::getAppValue('rainloop', 'rainloop-url', '')); + $oTemplate->assign('rainloop-path', OCP\Config::getAppValue('rainloop', 'rainloop-path', '')); +} + +$oTemplate->assign('rainloop-autologin', OCP\Config::getAppValue('rainloop', 'rainloop-autologin', false)); +return $oTemplate->fetchPage(); diff --git a/build/owncloud/rainloop-app/ajax/admin.php b/build/owncloud/rainloop-app/ajax/admin.php index fd35d61f4..8c935d93e 100644 --- a/build/owncloud/rainloop-app/ajax/admin.php +++ b/build/owncloud/rainloop-app/ajax/admin.php @@ -4,7 +4,7 @@ * ownCloud - RainLoop mail plugin * * @author RainLoop Team - * @copyright 2014 RainLoop Team + * @copyright 2015 RainLoop Team * * https://github.com/RainLoop/rainloop-webmail/tree/master/build/owncloud */ @@ -17,22 +17,32 @@ $sUrl = ''; $sPath = ''; $bAutologin = false; -if (isset($_POST['appname'], $_POST['rainloop-url'], $_POST['rainloop-path']) && 'rainloop' === $_POST['appname']) +$bInstalledLocaly = file_exists(__DIR__.'/../app/index.php'); + +if (isset($_POST['appname']) && 'rainloop' === $_POST['appname'] && + ($bInstalledLocaly ? true : isset($_POST['rainloop-url'], $_POST['rainloop-path']))) { - OCP\Config::setAppValue('rainloop', 'rainloop-url', $_POST['rainloop-url']); - OCP\Config::setAppValue('rainloop', 'rainloop-path', $_POST['rainloop-path']); OCP\Config::setAppValue('rainloop', 'rainloop-autologin', isset($_POST['rainloop-autologin']) ? '1' === $_POST['rainloop-autologin'] : false); - $sUrl = OCP\Config::getAppValue('rainloop', 'rainloop-url', ''); - $sPath = OCP\Config::getAppValue('rainloop', 'rainloop-path', ''); + if (!$bInstalledLocaly) + { + OCP\Config::setAppValue('rainloop', 'rainloop-url', $_POST['rainloop-url']); + OCP\Config::setAppValue('rainloop', 'rainloop-path', $_POST['rainloop-path']); + + $sUrl = OCP\Config::getAppValue('rainloop', 'rainloop-url', ''); + $sPath = OCP\Config::getAppValue('rainloop', 'rainloop-path', ''); + } + $bAutologin = OCP\Config::getAppValue('rainloop', 'rainloop-autologin', false); } else { + sleep(1); OC_JSON::error(array('Message' => 'Invalid Argument(s)', 'Url' => $sUrl, 'Path' => $sPath)); return false; } +sleep(1); OCP\JSON::success(array('Message' => 'Saved successfully', 'Url' => $sUrl, 'Path' => $sPath)); return true; diff --git a/build/owncloud/rainloop-app/ajax/personal.php b/build/owncloud/rainloop-app/ajax/personal.php index 3fcf22fb7..4ea738417 100644 --- a/build/owncloud/rainloop-app/ajax/personal.php +++ b/build/owncloud/rainloop-app/ajax/personal.php @@ -4,7 +4,7 @@ * ownCloud - RainLoop mail plugin * * @author RainLoop Team - * @copyright 2014 RainLoop Team + * @copyright 2015 RainLoop Team * * https://github.com/RainLoop/rainloop-webmail/tree/master/build/owncloud */ @@ -37,9 +37,11 @@ if (isset($_POST['appname'], $_POST['rainloop-password'], $_POST['rainloop-email } else { + sleep(1); OC_JSON::error(array('Message' => 'Invalid argument(s)', 'Email' => $sEmail)); return false; } +sleep(1); OCP\JSON::success(array('Message' => 'Saved successfully', 'Email' => $sEmail)); return true; diff --git a/build/owncloud/rainloop-app/app.php b/build/owncloud/rainloop-app/app.php new file mode 100644 index 000000000..94fdee86e --- /dev/null +++ b/build/owncloud/rainloop-app/app.php @@ -0,0 +1,10 @@ + rainloop RainLoop - RainLoop Webmail + Simple, modern & fast web-based email client. + +Modest system requirements, decent performance, simple installation and upgrade, no database required - +all these make RainLoop Webmail a perfect choice for your email solution. 0.0 CC BY-NC-SA 3.0 RainLoop Team diff --git a/build/owncloud/rainloop-app/appinfo/routes.php b/build/owncloud/rainloop-app/appinfo/routes.php new file mode 100644 index 000000000..7716e0a74 --- /dev/null +++ b/build/owncloud/rainloop-app/appinfo/routes.php @@ -0,0 +1,13 @@ +create('rainloop_index', '/') + ->actionInclude('rainloop/index.php'); + +$this->create('rainloop_app', '/app/') + ->actionInclude('rainloop/app.php'); + +$this->create('rainloop_ajax_personal', 'ajax/personal.php') + ->actionInclude('rainloop/ajax/personal.php'); + +$this->create('rainloop_ajax_admin', 'ajax/admin.php') + ->actionInclude('rainloop/ajax/admin.php'); diff --git a/build/owncloud/rainloop-app/index.php b/build/owncloud/rainloop-app/index.php index a958d631a..c38d27def 100644 --- a/build/owncloud/rainloop-app/index.php +++ b/build/owncloud/rainloop-app/index.php @@ -4,7 +4,7 @@ * ownCloud - RainLoop mail plugin * * @author RainLoop Team - * @copyright 2014 RainLoop Team + * @copyright 2015 RainLoop Team * * https://github.com/RainLoop/rainloop-webmail/tree/master/build/owncloud */ @@ -13,8 +13,23 @@ OCP\User::checkLoggedIn(); OCP\App::checkAppEnabled('rainloop'); OCP\App::setActiveNavigationEntry('rainloop_index'); -$sUrl = trim(OCP\Config::getAppValue('rainloop', 'rainloop-url', '')); -$sPath = trim(OCP\Config::getAppValue('rainloop', 'rainloop-path', '')); +include_once OC_App::getAppPath('rainloop').'/lib/RainLoopHelper.php'; + +$sUrl = ''; +$sPath = ''; + +$bInstalledLocaly = file_exists(__DIR__.'/app/index.php'); +if ($bInstalledLocaly) +{ + $sUrl = OC_RainLoop_Helper::getAppUrl(); + $sPath = __DIR__.'/app/'; +} +else +{ + $sUrl = trim(OCP\Config::getAppValue('rainloop', 'rainloop-url', '')); + $sPath = trim(OCP\Config::getAppValue('rainloop', 'rainloop-path', '')); +} + $bAutologin = OCP\Config::getAppValue('rainloop', 'rainloop-autologin', false); if ('' === $sUrl || '' === $sPath) @@ -23,10 +38,6 @@ if ('' === $sUrl || '' === $sPath) } else { - include_once OC_App::getAppPath('rainloop').'/lib/RainLoopHelper.php'; - - OC_Config::setValue('xframe_restriction', false); - $sUser = OCP\User::getUser(); if ($bAutologin) diff --git a/build/owncloud/rainloop-app/js/admin.js b/build/owncloud/rainloop-app/js/admin.js index c47ed8b2c..a7a2bc4be 100755 --- a/build/owncloud/rainloop-app/js/admin.js +++ b/build/owncloud/rainloop-app/js/admin.js @@ -1,13 +1,13 @@ - -/** - * ownCloud - RainLoop mail plugin - * - * @author RainLoop Team - * @copyright 2014 RainLoop Team - * - * https://github.com/RainLoop/rainloop-webmail/tree/master/build/owncloud - */ - -$(function() { - RainLoopFormHelper('#mail-rainloop-admin-form', 'admin.php'); -}); + +/** + * ownCloud - RainLoop mail plugin + * + * @author RainLoop Team + * @copyright 2015 RainLoop Team + * + * https://github.com/RainLoop/rainloop-webmail/tree/master/build/owncloud + */ + +$(function() { + RainLoopFormHelper('#mail-rainloop-admin-form', 'admin.php'); +}); diff --git a/build/owncloud/rainloop-app/js/personal.js b/build/owncloud/rainloop-app/js/personal.js index 008bf41b4..ab65be751 100755 --- a/build/owncloud/rainloop-app/js/personal.js +++ b/build/owncloud/rainloop-app/js/personal.js @@ -1,13 +1,13 @@ - -/** - * ownCloud - RainLoop mail plugin - * - * @author RainLoop Team - * @copyright 2014 RainLoop Team - * - * https://github.com/RainLoop/rainloop-webmail/tree/master/build/owncloud - */ - -$(function() { - RainLoopFormHelper('#mail-rainloop-personal-form', 'personal.php'); -}); + +/** + * ownCloud - RainLoop mail plugin + * + * @author RainLoop Team + * @copyright 2015 RainLoop Team + * + * https://github.com/RainLoop/rainloop-webmail/tree/master/build/owncloud + */ + +$(function() { + RainLoopFormHelper('#mail-rainloop-personal-form', 'personal.php'); +}); diff --git a/build/owncloud/rainloop-app/lib/RainLoopHelper.php b/build/owncloud/rainloop-app/lib/RainLoopHelper.php index cc67e8c3d..eb3d1cdf5 100644 --- a/build/owncloud/rainloop-app/lib/RainLoopHelper.php +++ b/build/owncloud/rainloop-app/lib/RainLoopHelper.php @@ -2,6 +2,18 @@ class OC_RainLoop_Helper { + /** + * @return string + */ + public static function getAppUrl() + { + $sRequestUri = empty($_SERVER['REQUEST_URI']) ? '': trim($_SERVER['REQUEST_URI']); + $sRequestUri = preg_replace('/index.php\/.+$/', 'index.php/', $sRequestUri); + $sRequestUri = $sRequestUri.'apps/rainloop/app/'; + + return '/'.ltrim($sRequestUri, '/\\'); + } + /** * @param string $sPath * @param string $sEmail @@ -16,6 +28,8 @@ class OC_RainLoop_Helper $sPath = rtrim(trim($sPath), '\\/').'/index.php'; if (file_exists($sPath)) { + self::regRainLoopDataFunction(); + $_ENV['RAINLOOP_INCLUDE_AS_API'] = true; include $sPath; @@ -124,4 +138,102 @@ class OC_RainLoop_Helper return false; } + + public static function regRainLoopDataFunction() + { + if (!@function_exists('__get_custom_data_full_path')) + { + $_ENV['RAINLOOP_OWNCLOUD'] = true; + + function __get_custom_data_full_path() + { + $sData = __DIR__.'/../../data/'; + if (class_exists('OC_Config')) + { + $sData = rtrim(trim(OC_Config::getValue('datadirectory', '')), '\\/').'/'; + } + + return @is_dir($sData) ? $sData.'rainloop-storage' : ''; + } + } + } + + public static function mimeContentType($filename) { + + $mime_types = array( + + 'woff' => 'application/font-woff', + + 'txt' => 'text/plain', + 'htm' => 'text/html', + 'html' => 'text/html', + 'php' => 'text/html', + 'css' => 'text/css', + 'js' => 'application/javascript', + 'json' => 'application/json', + 'xml' => 'application/xml', + 'swf' => 'application/x-shockwave-flash', + 'flv' => 'video/x-flv', + + // images + 'png' => 'image/png', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'gif' => 'image/gif', + 'bmp' => 'image/bmp', + 'ico' => 'image/vnd.microsoft.icon', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'svg' => 'image/svg+xml', + 'svgz' => 'image/svg+xml', + + // archives + 'zip' => 'application/zip', + 'rar' => 'application/x-rar-compressed', + 'exe' => 'application/x-msdownload', + 'msi' => 'application/x-msdownload', + 'cab' => 'application/vnd.ms-cab-compressed', + + // audio/video + 'mp3' => 'audio/mpeg', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + + // adobe + 'pdf' => 'application/pdf', + 'psd' => 'image/vnd.adobe.photoshop', + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'ps' => 'application/postscript', + + // ms office + 'doc' => 'application/msword', + 'rtf' => 'application/rtf', + 'xls' => 'application/vnd.ms-excel', + 'ppt' => 'application/vnd.ms-powerpoint', + + // open office + 'odt' => 'application/vnd.oasis.opendocument.text', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + ); + + if (0 < strpos($filename, '.')) + { + $ext = strtolower(array_pop(explode('.',$filename))); + if (array_key_exists($ext, $mime_types)) + { + return $mime_types[$ext]; + } + else if (function_exists('finfo_open')) + { + $finfo = finfo_open(FILEINFO_MIME); + $mimetype = finfo_file($finfo, $filename); + finfo_close($finfo); + return $mimetype; + } + } + + return 'application/octet-stream'; + } } diff --git a/build/owncloud/rainloop-app/personal.php b/build/owncloud/rainloop-app/personal.php index 31ca9c409..0499d520e 100644 --- a/build/owncloud/rainloop-app/personal.php +++ b/build/owncloud/rainloop-app/personal.php @@ -4,7 +4,7 @@ * ownCloud - RainLoop mail plugin * * @author RainLoop Team - * @copyright 2014 RainLoop Team + * @copyright 2015 RainLoop Team * * https://github.com/RainLoop/rainloop-webmail/tree/master/build/owncloud */ @@ -14,11 +14,19 @@ OCP\App::checkAppEnabled('rainloop'); OCP\Util::addScript('rainloop', 'personal'); -$sUrl = trim(OCP\Config::getAppValue('rainloop', 'rainloop-url', '')); -$sPath = trim(OCP\Config::getAppValue('rainloop', 'rainloop-path', '')); +$sUrl = ''; +$sPath = ''; + $bAutologin = OCP\Config::getAppValue('rainloop', 'rainloop-autologin', false); -if ($bAutologin || '' === $sUrl || '' === $sPath) +$bInstalledLocaly = file_exists(__DIR__.'/app/index.php'); +if (!$bInstalledLocaly) +{ + $sUrl = trim(OCP\Config::getAppValue('rainloop', 'rainloop-url', '')); + $sPath = trim(OCP\Config::getAppValue('rainloop', 'rainloop-path', '')); +} + +if ($bAutologin || (!$bInstalledLocaly && ('' === $sUrl || '' === $sPath))) { $oTemplate = new OCP\Template('rainloop', 'empty'); } diff --git a/build/owncloud/rainloop-app/templates/admin-local.php b/build/owncloud/rainloop-app/templates/admin-local.php new file mode 100644 index 000000000..27a90acf2 --- /dev/null +++ b/build/owncloud/rainloop-app/templates/admin-local.php @@ -0,0 +1,29 @@ +
+
+ + + +
+

t('RainLoop Webmail')); ?>

+
+ +

+ + t('Go to RainLoop Webmail admin panel')); ?> + +

+
+ +

+ checked="checked" /> + +
+
+ +    +

+
+
+
\ No newline at end of file diff --git a/dev/App/Abstract.js b/dev/App/Abstract.js index 95d5e1485..5eb1ebd46 100644 --- a/dev/App/Abstract.js +++ b/dev/App/Abstract.js @@ -50,6 +50,37 @@ } }); + Globals.$win.on('resize', function () { + Events.pub('window.resize'); + }); + + Events.sub('window.resize', _.throttle(function () { + + var + iH = Globals.$win.height(), + iW = Globals.$win.height() + ; + + if (Globals.$win.__sizes[0] !== iH || Globals.$win.__sizes[1] !== iW) + { + Globals.$win.__sizes[0] = iH; + Globals.$win.__sizes[1] = iW; + + Events.pub('window.resize.real'); + } + + }, 50)); + + // TODO +// Events.sub({ +// 'window.resize': function () { +// window.console.log('window.resize'); +// }, +// 'window.resize.real': function () { +// window.console.log('window.resize.real'); +// } +// }); + Globals.$doc.on('keydown', function (oEvent) { if (oEvent && oEvent.ctrlKey) { @@ -315,6 +346,8 @@ ssm.ready(); + + require('Stores/Language').populate(); require('Stores/Theme').populate(); require('Stores/Social').populate(); diff --git a/dev/App/User.js b/dev/App/User.js index b4a2792f2..9361f2af0 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -41,6 +41,7 @@ MessageModel = require('Model/Message'), AccountModel = require('Model/Account'), IdentityModel = require('Model/Identity'), + TemplateModel = require('Model/Template'), OpenPgpKeyModel = require('Model/OpenPgpKey'), AbstractApp = require('App/Abstract') @@ -611,12 +612,10 @@ { Utils.delegateRunOnDestroy(TemplateStore.templates()); - IdentityStore.templates(_.map(oData.Result['Templates'], function (oIdentityData) { - return { - 'id': Utils.pString(oIdentityData['Id']), - 'name': Utils.pString(oIdentityData['Name']) - }; - })); + TemplateStore.templates(_.compact(_.map(oData.Result['Templates'], function (oTemplateData) { + var oTemplate = new TemplateModel(); + return oTemplate.parse(oTemplateData) ? oTemplate : null; + }))); } }); }; diff --git a/dev/Common/Enums.js b/dev/Common/Enums.js index 4a5c157ed..77851e32e 100644 --- a/dev/Common/Enums.js +++ b/dev/Common/Enums.js @@ -45,6 +45,7 @@ 'Sieve': 'SIEVE', 'Filters': 'FILTERS', 'AttachmentThumbnails': 'ATTACHMENT_THUMBNAILS', + 'Templates': 'TEMPLATES', 'AdditionalAccounts': 'ADDITIONAL_ACCOUNTS' }; diff --git a/dev/Common/Events.js b/dev/Common/Events.js index b8198fb1f..14b71d3a0 100644 --- a/dev/Common/Events.js +++ b/dev/Common/Events.js @@ -28,12 +28,24 @@ */ Events.prototype.sub = function (sName, fFunc, oContext) { - if (Utils.isUnd(this.oSubs[sName])) + if (Utils.isObject(sName)) { - this.oSubs[sName] = []; - } + oContext = fFunc || null; + fFunc = null; - this.oSubs[sName].push([fFunc, oContext]); + _.each(sName, function (fSubFunc, sSubName) { + this.sub(sSubName, fSubFunc, oContext); + }, this); + } + else + { + if (Utils.isUnd(this.oSubs[sName])) + { + this.oSubs[sName] = []; + } + + this.oSubs[sName].push([fFunc, oContext]); + } return this; }; diff --git a/dev/Common/Globals.js b/dev/Common/Globals.js index 752d02797..71ada0360 100644 --- a/dev/Common/Globals.js +++ b/dev/Common/Globals.js @@ -20,6 +20,8 @@ Globals.$html = $('html'); Globals.$div = $('
'); + Globals.$win.__sizes = [0, 0]; + /** * @type {?} */ @@ -255,7 +257,7 @@ }); Globals.keyScopeReal.subscribe(function (sValue) { -// window.console.log(sValue); +// window.console.log(sValue); //TODO key.setScope(sValue); }); diff --git a/dev/Common/HtmlEditor.js b/dev/Common/HtmlEditor.js index ead03e929..b039de237 100644 --- a/dev/Common/HtmlEditor.js +++ b/dev/Common/HtmlEditor.js @@ -106,8 +106,13 @@ .replace("\u0002", '').replace("\u0002", '') .replace("\u0003", '').replace("\u0003", '') .replace("\u0003", '').replace("\u0003", '') + .replace("\u0004", '').replace("\u0004", '') + .replace("\u0004", '').replace("\u0004", '') + .replace("\u0005", '').replace("\u0005", '') + .replace("\u0005", '').replace("\u0005", '') ; - } + }; + /** * @param {boolean=} bWrapIsHtml = false * @param {boolean=} bClearSignatureSigns = false diff --git a/dev/Common/Utils.js b/dev/Common/Utils.js index 0c75e5d8e..f53db4e5b 100644 --- a/dev/Common/Utils.js +++ b/dev/Common/Utils.js @@ -24,6 +24,7 @@ Utils.trim = $.trim; Utils.inArray = $.inArray; Utils.isArray = _.isArray; + Utils.isObject = _.isObject; Utils.isFunc = _.isFunction; Utils.isUnd = _.isUndefined; Utils.isNull = _.isNull; diff --git a/dev/Knoin/AbstractModel.js b/dev/Knoin/AbstractModel.js index cc9a0161b..67ce5cd86 100644 --- a/dev/Knoin/AbstractModel.js +++ b/dev/Knoin/AbstractModel.js @@ -41,7 +41,7 @@ AbstractModel.prototype.onDestroy = function () { Utils.disposeObject(this); -// window.console.log('onDestroy: ' + this.sModelName); +// window.console.log('onDestroy: ' + this.sModelName); // TODO }; module.exports = AbstractModel; diff --git a/dev/Model/Template.js b/dev/Model/Template.js index e8b53ae1d..765802dc2 100644 --- a/dev/Model/Template.js +++ b/dev/Model/Template.js @@ -7,6 +7,8 @@ _ = require('_'), ko = require('ko'), + Utils = require('Common/Utils'), + AbstractModel = require('Knoin/AbstractModel') ; @@ -24,6 +26,7 @@ this.id = sID; this.name = sName; this.body = sBody; + this.populated = true; this.deleteAccess = ko.observable(false); } @@ -45,6 +48,30 @@ */ TemplateModel.prototype.body = ''; + /** + * @type {boolean} + */ + TemplateModel.prototype.populated = true; + + /** + * @type {boolean} + */ + TemplateModel.prototype.parse = function (oItem) + { + var bResult = false; + if (oItem && 'Object/Template' === oItem['@Object']) + { + this.id = Utils.pString(oItem['ID']); + this.name = Utils.pString(oItem['Name']); + this.body = Utils.pString(oItem['Body']); + this.populated = !!oItem['Populated']; + + bResult = true; + } + + return bResult; + }; + module.exports = TemplateModel; }()); \ No newline at end of file diff --git a/dev/Screen/User/Settings.js b/dev/Screen/User/Settings.js index 201d394c6..473fed7e2 100644 --- a/dev/Screen/User/Settings.js +++ b/dev/Screen/User/Settings.js @@ -85,8 +85,11 @@ 'SettingsChangePassword', 'SETTINGS_LABELS/LABEL_CHANGE_PASSWORD_NAME', 'change-password'); } -// kn.addSettingsViewModel(require('Settings/User/Templates'), -// 'SettingsTemplates', 'SETTINGS_LABELS/LABEL_TEMPLATES_NAME', 'templates'); + if (Settings.capa(Enums.Capa.Templates)) + { + kn.addSettingsViewModel(require('Settings/User/Templates'), + 'SettingsTemplates', 'SETTINGS_LABELS/LABEL_TEMPLATES_NAME', 'templates'); + } kn.addSettingsViewModel(require('Settings/User/Folders'), 'SettingsFolders', 'SETTINGS_LABELS/LABEL_FOLDERS_NAME', 'folders'); diff --git a/dev/Settings/Admin/General.js b/dev/Settings/Admin/General.js index 929b1d388..5e80bfb4b 100644 --- a/dev/Settings/Admin/General.js +++ b/dev/Settings/Admin/General.js @@ -34,6 +34,7 @@ this.capaGravatar = CapaAdminStore.gravatar; this.capaAdditionalAccounts = CapaAdminStore.additionalAccounts; this.capaAttachmentThumbnails = CapaAdminStore.attachmentThumbnails; + this.capaTemplates = CapaAdminStore.templates; this.allowLanguagesOnSettings = AppAdminStore.allowLanguagesOnSettings; this.weakPassword = AppAdminStore.weakPassword; @@ -105,6 +106,12 @@ }); }); + self.capaTemplates.subscribe(function (bValue) { + Remote.saveAdminConfig(null, { + 'CapaTemplates': bValue ? '1' : '0' + }); + }); + self.capaGravatar.subscribe(function (bValue) { Remote.saveAdminConfig(null, { 'CapaGravatar': bValue ? '1' : '0' diff --git a/dev/Settings/User/Accounts.js b/dev/Settings/User/Accounts.js index 43ffb65f6..ac49ffb26 100644 --- a/dev/Settings/User/Accounts.js +++ b/dev/Settings/User/Accounts.js @@ -9,7 +9,6 @@ ko = require('ko'), Enums = require('Common/Enums'), - Utils = require('Common/Utils'), Translator = require('Common/Translator'), Links = require('Common/Links'), diff --git a/dev/Storage/User/Remote.js b/dev/Storage/User/Remote.js index a9b6178e5..c53c713f2 100644 --- a/dev/Storage/User/Remote.js +++ b/dev/Storage/User/Remote.js @@ -293,7 +293,17 @@ /** * @param {?Function} fCallback */ - RemoteUserStorage.prototype.templateGetById = function (fCallback, sID, sName, sBody) + RemoteUserStorage.prototype.templateDelete = function (fCallback, sID) + { + this.defaultRequest(fCallback, 'TemplateDelete', { + 'IdToDelete': sID + }); + }; + + /** + * @param {?Function} fCallback + */ + RemoteUserStorage.prototype.templateSetup = function (fCallback, sID, sName, sBody) { this.defaultRequest(fCallback, 'TemplateSetup', { 'ID': sID, diff --git a/dev/Stores/Admin/Capa.js b/dev/Stores/Admin/Capa.js index e473d145b..55b7886cc 100644 --- a/dev/Stores/Admin/Capa.js +++ b/dev/Stores/Admin/Capa.js @@ -25,6 +25,7 @@ this.userBackground = ko.observable(false); this.openPGP = ko.observable(false); this.twoFactorAuth = ko.observable(false); + this.templates = ko.observable(false); } CapaAdminStore.prototype.populate = function() @@ -38,6 +39,7 @@ this.userBackground(Settings.capa(Enums.Capa.UserBackground)); this.openPGP(Settings.capa(Enums.Capa.OpenPGP)); this.twoFactorAuth(Settings.capa(Enums.Capa.TwoFactor)); + this.templates(Settings.capa(Enums.Capa.Templates)); }; module.exports = new CapaAdminStore(); diff --git a/dev/Stores/User/Template.js b/dev/Stores/User/Template.js index 8a3f41030..cfaff8986 100644 --- a/dev/Stores/User/Template.js +++ b/dev/Stores/User/Template.js @@ -5,9 +5,9 @@ var _ = require('_'), - ko = require('ko'), + ko = require('ko') - Remote = require('Storage/User/Remote') +// Remote = require('Storage/User/Remote') ; /** @@ -27,16 +27,16 @@ }))); }, this); - this.templatesNames.subscribe(function (aList) { - if (this.templatesNames.skipFirst) - { - this.templatesNames.skipFirst = false; - } - else if (aList && 1 < aList.length) - { - Remote.templatesSortOrder(null, aList); - } - }, this); +// this.templatesNames.subscribe(function (aList) { +// if (this.templatesNames.skipFirst) +// { +// this.templatesNames.skipFirst = false; +// } +// else if (aList && 1 < aList.length) +// { +// Remote.templatesSortOrder(null, aList); +// } +// }, this); } module.exports = new TemplateUserStore(); diff --git a/dev/Styles/SettingsTemplates.less b/dev/Styles/SettingsTemplates.less index 85718eba1..2aa1afb30 100644 --- a/dev/Styles/SettingsTemplates.less +++ b/dev/Styles/SettingsTemplates.less @@ -1,5 +1,5 @@ -.b-settings-identities { +.b-settings-templates { .process-place { text-align: center; diff --git a/dev/Styles/Template.less b/dev/Styles/Template.less index 6ab167d76..5da4d9f1e 100644 --- a/dev/Styles/Template.less +++ b/dev/Styles/Template.less @@ -2,11 +2,15 @@ .b-template-add-content { &.modal { - width: 700px; + width: 750px; } .modal-header { background-color: #fff; } + + .e-template-place { + height: 300px; + } } } diff --git a/dev/View/Popup/Compose.js b/dev/View/Popup/Compose.js index 2cfce8863..aa824b90d 100644 --- a/dev/View/Popup/Compose.js +++ b/dev/View/Popup/Compose.js @@ -80,7 +80,7 @@ this.bFromDraft = false; this.sReferences = ''; - this.triggerForResize = _.bind(this.triggerForResize, this); + this.resizerTrigger = _.bind(this.resizerTrigger, this); this.allowContacts = !!AppStore.contactsIsAllowed(); @@ -90,8 +90,6 @@ this.capaOpenPGP = PgpStore.capaOpenPGP; - this.resizer = ko.observable(false).extend({'throttle': 50}); - this.identitiesDropdownTrigger = ko.observable(false); this.to = ko.observable(''); @@ -241,11 +239,13 @@ } }, this); - this.editorResizeThrottle = _.throttle(_.bind(this.editorResize, this), 100); + this.resizer = ko.observable(false).extend({'throttle': 50}); - this.resizer.subscribe(function () { - this.editorResizeThrottle(); - }, this); + this.resizer.subscribe(_.bind(function () { + if (this.oEditor){ + this.oEditor.resize(); + } + }, this)); this.canBeSendedOrSaved = ko.computed(function () { return !this.sending() && !this.saving(); @@ -429,9 +429,9 @@ } }, this); - this.showCc.subscribe(this.triggerForResize); - this.showBcc.subscribe(this.triggerForResize); - this.showReplyTo.subscribe(this.triggerForResize); + this.showCc.subscribe(this.resizerTrigger); + this.showBcc.subscribe(this.resizerTrigger); + this.showReplyTo.subscribe(this.resizerTrigger); this.dropboxEnabled = SocialStore.dropbox.enabled; this.dropboxApiKey = SocialStore.dropbox.apiKey; @@ -1230,7 +1230,7 @@ this.currentIdentity(oIdentity); } - this.triggerForResize(); + this.resizerTrigger(); }; ComposePopupView.prototype.onFocus = function () @@ -1244,15 +1244,7 @@ this.oEditor.focus(); } - this.triggerForResize(); - }; - - ComposePopupView.prototype.editorResize = function () - { - if (this.oEditor) - { - this.oEditor.resize(); - } + this.resizerTrigger(); }; ComposePopupView.prototype.tryToClosePopup = function () @@ -1315,7 +1307,7 @@ return false; }); - Globals.$win.on('resize', self.triggerForResize); + Events.sub('window.resize.real', this.resizerTrigger); if (this.dropboxEnabled()) { @@ -2034,10 +2026,9 @@ }); }; - ComposePopupView.prototype.triggerForResize = function () + ComposePopupView.prototype.resizerTrigger = function () { this.resizer(!this.resizer()); - this.editorResizeThrottle(); }; module.exports = ComposePopupView; diff --git a/dev/View/Popup/Template.js b/dev/View/Popup/Template.js index da43d16a3..5b745d4b0 100644 --- a/dev/View/Popup/Template.js +++ b/dev/View/Popup/Template.js @@ -36,6 +36,7 @@ this.name.focus = ko.observable(false); this.body = ko.observable(''); + this.body.loading = ko.observable(false); this.body.error = ko.observable(false); this.name.subscribe(function () { @@ -51,6 +52,8 @@ this.addTemplateCommand = Utils.createCommand(this, function () { + this.populateBodyFromEditor(); + this.name.error('' === Utils.trim(this.name())); this.body.error('' === Utils.trim(this.body()) || ':HTML:' === Utils.trim(this.body())); @@ -109,40 +112,52 @@ this.submitRequest(false); this.submitError(''); + + if (this.editor) + { + this.setBody(''); + } + }; + + TemplatePopupView.prototype.setBody = function (sBody) + { + if (this.editor) + { + if (':HTML:' === sBody.substr(0, 6)) + { + this.editor.setHtml(sBody.substr(6), false); + } + else + { + this.editor.setPlain(sBody, false); + } + } + }; + + TemplatePopupView.prototype.populateBodyFromEditor = function () + { + if (this.editor) + { + this.body( + (this.editor.isHtml() ? ':HTML:' : '') + this.editor.getData() + ); + } }; TemplatePopupView.prototype.editorSetBody = function (sBody) { - var - self = this, - fEditorSetData = function (sBody) { - if (self.editor) - { - if (':HTML:' === sBody.substr(0, 6)) - { - self.editor.setHtml(sBody.substr(6), false); - } - else - { - self.editor.setPlain(sBody, false); - } - } - } - ; - if (!this.editor && this.signatureDom()) { + var self = this; this.editor = new HtmlEditor(self.signatureDom(), function () { - self.body( - (self.editor.isHtml() ? ':HTML:' : '') + self.editor.getData() - ); + self.populateBodyFromEditor(); }, function () { - fEditorSetData(sBody); + self.setBody(sBody); }); } - else if (this.editor) + else { - fEditorSetData(sBody); + this.setBody(sBody); } }; @@ -156,16 +171,44 @@ { this.id(oTemplate.id); this.name(oTemplate.name); + this.body(oTemplate.body); - this.body.loading(true); + if (oTemplate.populated) + { + self.editorSetBody(this.body()); + } + else + { + this.body.loading(true); + self.body.error(false); - Remote.templateGetById(function () { + Remote.templateGetById(function (sResult, oData) { - self.body.loading(false); + self.body.loading(false); - self.editorSetBody(''); + if (Enums.StorageResultType.Success === sResult && oData && oData.Result && + 'Object/Template' === oData.Result['@Object'] && Utils.isNormal(oData.Result['Body'])) + { + oTemplate.body = oData.Result['Body']; + oTemplate.populated = true; - }, this.id()); + self.body(oTemplate.body); + self.body.error(false); + } + else + { + self.body(''); + self.body.error(true); + } + + self.editorSetBody(self.body()); + + }, this.id()); + } + } + else + { + self.editorSetBody(''); } }; diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js index 8f74fbcb2..980f7ada8 100644 --- a/dev/View/User/MailBox/MessageView.js +++ b/dev/View/User/MailBox/MessageView.js @@ -423,11 +423,11 @@ this.showFullInfo.subscribe(fCheckHeaderHeight); this.message.subscribe(fCheckHeaderHeight); - Globals.$win.on('resize', function () { + Events.sub('window.resize', _.throttle(function () { _.delay(fCheckHeaderHeight, 1); _.delay(fCheckHeaderHeight, 200); _.delay(fCheckHeaderHeight, 500); - }); + }, 50)); this.showFullInfo.subscribe(function () { Utils.windowResize(); diff --git a/gulpfile.js b/gulpfile.js index 41ca2c310..dd0ada027 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -7,6 +7,7 @@ var devVersion: '0.0.0', releasesPath: 'build/dist/releases', + rainloopBuilded: false, destPath: '', cleanPath: '', zipSrcPath: '', @@ -459,6 +460,8 @@ gulp.task('rainloop:setup', ['rainloop:copy'], function() { cfg.zipSrcPath = dist; cfg.zipFile = 'rainloop-' + versionFull + '.zip'; cfg.md5File = cfg.zipFile; + + cfg.rainloopBuilded = true; }); gulp.task('rainloop:zip', ['rainloop:copy', 'rainloop:setup'], function() { @@ -490,7 +493,28 @@ gulp.task('rainloop:owncloud:copy', function() { .pipe(gulp.dest(dist + 'rainloop')); }); -gulp.task('rainloop:owncloud:setup', ['rainloop:owncloud:copy'], function() { +gulp.task('rainloop:owncloud:copy-rainloop', ['rainloop:start', 'rainloop:owncloud:copy'], function() { + + var + versionFull = pkg.ownCloudPackageVersion, + dist = cfg.releasesPath + '/owncloud/' + versionFull + '/src/rainloop/' + ; + + if (cfg.rainloopBuilded && cfg.destPath) + { + return gulp.src(cfg.destPath + '/src/**/*', {base: cfg.destPath + '/src/'}) + .pipe(gulp.dest(dist + 'app/')); + } + + return true; +}); + +gulp.task('rainloop:owncloud:copy-rainloop:clean', ['rainloop:owncloud:copy-rainloop'], function() { + return (cfg.cleanPath) ? cleanDir(cfg.cleanPath) : false; +}); + +gulp.task('rainloop:owncloud:setup', ['rainloop:owncloud:copy', + 'rainloop:owncloud:copy-rainloop'], function() { var versionFull = pkg.ownCloudPackageVersion, @@ -529,8 +553,12 @@ gulp.task('rainloop:owncloud:clean', ['rainloop:owncloud:copy', 'rainloop:ownclo gulp.task('default', ['js:libs', 'js:boot', 'js:openpgp', 'js:min', 'css:main:min', 'ckeditor', 'fontastic']); gulp.task('fast', ['js:app', 'js:admin', 'js:chunks', 'css:main']); -gulp.task('rainloop', ['js:lint', 'rainloop:copy', 'rainloop:setup', 'rainloop:zip', 'rainloop:md5', 'rainloop:clean']); -gulp.task('owncloud', ['rainloop:owncloud:copy', 'rainloop:owncloud:setup', 'rainloop:owncloud:zip', 'rainloop:owncloud:md5', 'rainloop:owncloud:clean']); +gulp.task('rainloop:start', ['js:lint', 'rainloop:copy', 'rainloop:setup']); +gulp.task('rainloop', ['rainloop:start', 'rainloop:zip', 'rainloop:md5', 'rainloop:clean']); + +gulp.task('owncloud', ['rainloop:owncloud:copy', + 'rainloop:owncloud:copy-rainloop', + 'rainloop:owncloud:setup', 'rainloop:owncloud:zip', 'rainloop:owncloud:md5', 'rainloop:owncloud:clean']); //WATCH gulp.task('watch', ['fast'], function() { diff --git a/package.json b/package.json index e65fe165d..90e8cb481 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "RainLoop", "title": "RainLoop Webmail", - "version": "1.8.0", - "release": "251", + "version": "1.8.1", + "release": "255", "description": "Simple, modern & fast web-based email client", "homepage": "http://rainloop.net", "main": "gulpfile.js", @@ -36,7 +36,7 @@ "plugins" ], "readmeFilename": "README.md", - "ownCloudPackageVersion": "2.3", + "ownCloudPackageVersion": "3.0", "engines": { "node": ">= 0.10.0" }, diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Base/Http.php b/rainloop/v/0.0.0/app/libraries/MailSo/Base/Http.php index 40d74653d..fb6b94e47 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Base/Http.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Base/Http.php @@ -263,7 +263,6 @@ class Http */ public function GetHeader($sHeader) { - $sResultHeader = ''; $sServerKey = 'HTTP_'.\strtoupper(\str_replace('-', '_', $sHeader)); $sResultHeader = $this->GetServer($sServerKey, ''); diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Base/StreamWrappers/SubStreams.php b/rainloop/v/0.0.0/app/libraries/MailSo/Base/StreamWrappers/SubStreams.php index 4edf61254..cd967af6c 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Base/StreamWrappers/SubStreams.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Base/StreamWrappers/SubStreams.php @@ -65,7 +65,7 @@ class SubStreams \stream_wrapper_register(self::STREAM_NAME, '\MailSo\Base\StreamWrappers\SubStreams'); } - $sHashName = \md5(\microtime(true).\rand(1000, 9999)); + $sHashName = \MailSo\Base\Utils::Md5Rand(); self::$aStreams[$sHashName] = $aSubStreams; @@ -84,7 +84,7 @@ class SubStreams { return $this->aSubStreams[$this->iIndex]; } - + return $nNull; } diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Base/Utils.php b/rainloop/v/0.0.0/app/libraries/MailSo/Base/Utils.php index 4d258380b..7e13d15bf 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Base/Utils.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Base/Utils.php @@ -2147,6 +2147,28 @@ class Utils return \is_string($mResult) && 0 < \strlen($mResult) ? $mResult : ''; } + /** + * @param string $sAdditionalSalt = '' + * + * @return string + */ + public static function Md5Rand($sAdditionalSalt = '') + { + return \md5(\microtime(true).\rand(10000, 99999). + \md5($sAdditionalSalt).\rand(10000, 99999).\microtime(true)); + } + + /** + * @param string $sAdditionalSalt = '' + * + * @return string + */ + public static function Sha1Rand($sAdditionalSalt = '') + { + return \sha1(\microtime(true).\rand(10000, 99999). + \sha1($sAdditionalSalt).\rand(10000, 99999).\microtime(true)); + } + /** * @param string $sData * @param string $sKey diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Log/Logger.php b/rainloop/v/0.0.0/app/libraries/MailSo/Log/Logger.php index 47cd599d1..3d513f170 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Log/Logger.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Log/Logger.php @@ -121,7 +121,7 @@ class Logger extends \MailSo\Base\Collection static $sCache = null; if (null === $sCache) { - $sCache = \substr(\md5(\microtime(true).\rand(10000, 99999)), -8); + $sCache = \substr(\MailSo\Base\Utils::Md5Rand(), -8); } return $sCache; diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php b/rainloop/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php index 24a25d95e..99c49d9fe 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php @@ -990,7 +990,7 @@ class MailClient { do { - $sKey = \md5(\rand(10000, 90000).\microtime(true)); + $sKey = \MailSo\Base\Utils::Md5Rand(); } while (isset($aCache[$sKey])); @@ -1006,7 +1006,7 @@ class MailClient { do { - $sKey = \md5(\rand(10000, 90000).\microtime(true)); + $sKey = \MailSo\Base\Utils::Md5Rand(); } while (isset($aCache[$sKey])); diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Mime/Message.php b/rainloop/v/0.0.0/app/libraries/MailSo/Mime/Message.php index 0b14d5625..8d0c99b12 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Mime/Message.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Mime/Message.php @@ -535,8 +535,9 @@ class Message $sHostName = 'localhost'; } - return '<'.\md5(\rand(100000, 999999).\time().$sHostName. - (\MailSo\Base\Utils::FunctionExistsAndEnabled('getmypid') ? @\getmypid() : '')).'@'.$sHostName.'>'; + return '<'. + \MailSo\Base\Utils::Md5Rand($sHostName. + (\MailSo\Base\Utils::FunctionExistsAndEnabled('getmypid') ? @\getmypid() : '')).'@'.$sHostName.'>'; } /** diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php index 7186607b4..d6dbab2b4 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php @@ -1511,7 +1511,7 @@ class Actions $aResult['UseLocalProxyForExternalImages'] = (bool) $oConfig->Get('labs', 'use_local_proxy_for_external_images', false); // user - $aResult['ShowImages'] = (bool) $oConfig->Get('webmail', 'show_images', false); + $aResult['ShowImages'] = (bool) $oConfig->Get('defaults', 'show_images', false); $aResult['MPP'] = (int) $oConfig->Get('webmail', 'messages_per_page', 25); $aResult['SoundNotification'] = false; $aResult['DesktopNotifications'] = false; @@ -1982,7 +1982,7 @@ class Actions */ private function generateSignMeToken($sEmail) { - return \md5(\microtime(true).APP_SALT.\rand(10000, 99999).$sEmail); + return \MailSo\Base\Utils::Md5Rand(APP_SALT.$sEmail); } /** @@ -2122,6 +2122,85 @@ class Actions return $aAccounts; } + /** + * @param \RainLoop\Model\Account $oAccount + * + * @return array + */ + public function GetTemplates($oAccount) + { + $aTemplates = array(); + if ($oAccount) + { + $aData = array(); + + $sData = $this->StorageProvider(true)->Get($oAccount, + \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, + 'templates' + ); + + if ('' !== $sData && '[' === \substr($sData, 0, 1)) + { + $aData = @\json_decode($sData, true); + } + + if (\is_array($aData) && 0 < \count($aData)) + { + foreach ($aData as $aItem) + { + $oItem = \RainLoop\Model\Template::NewInstance(); + $oItem->FromJSON($aItem); + + if ($oItem && $oItem->Validate()) + { + \array_push($aTemplates, $oItem); + } + } + } + + if (1 < \count($aTemplates)) + { + $sOrder = $this->StorageProvider()->Get($oAccount, + \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, + 'templates_order' + ); + + $aOrder = empty($sOrder) ? array() : @\json_decode($sOrder, true); + if (\is_array($aOrder) && 1 < \count($aOrder)) + { + \usort($aTemplates, function ($a, $b) use ($aOrder) { + return \array_search($a->Id(), $aOrder) < \array_search($b->Id(), $aOrder) ? -1 : 1; + }); + } + } + } + + return $aTemplates; + } + + /** + * @param \RainLoop\Model\Account $oAccount + * @param string $sID + * + * @return \RainLoop\Model\Identity + */ + public function GetTemplateByID($oAccount, $sID) + { + $aTemplates = $this->GetTemplates($oAccount); + if (\is_array($aTemplates)) + { + foreach ($aTemplates as $oIdentity) + { + if ($oIdentity && $sID === $oIdentity->Id()) + { + return $oIdentity; + } + } + } + + return isset($aTemplates[0]) ? $aTemplates[0] : null; + } + /** * @param \RainLoop\Model\Account $oAccount * @@ -2279,6 +2358,27 @@ class Actions ); } + /** + * @param \RainLoop\Model\Account $oAccount + * @param array $aTemplates = array() + * + * @return array + */ + public function SetTemplates($oAccount, $aTemplates = array()) + { + $aResult = array(); + foreach ($aTemplates as $oItem) + { + $aResult[] = $oItem->ToSimpleJSON(false); + } + + return $this->StorageProvider(true)->Put($oAccount, + \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, + 'templates', + @\json_encode($aResult) + ); + } + /** * @return array * @@ -2497,6 +2597,117 @@ class Actions return $this->DefaultResponse(__FUNCTION__, $this->SetIdentities($oAccount, $aNew)); } + /** + * @return array + * + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoTemplateSetup() + { + $oAccount = $this->getAccountFromToken(); + + if (!$this->GetCapa(false, \RainLoop\Enumerations\Capa::TEMPLATES, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $oTemplate = \RainLoop\Model\Template::NewInstance(); + if (!$oTemplate->FromJSON($this->GetActionParams(), true)) + { + throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::InvalidInputArgument); + } + + if ('' === $oTemplate->Id()) + { + $oTemplate->GenerateID(); + } + + $aTemplatesForSave = array(); + $aTemplates = $this->GetTemplates($oAccount); + + + foreach ($aTemplates as $oItem) + { + if ($oItem && $oItem->Id() !== $oTemplate->Id()) + { + $aTemplatesForSave[] = $oItem; + } + } + + $aTemplatesForSave[] = $oTemplate; + + return $this->DefaultResponse(__FUNCTION__, $this->SetTemplates($oAccount, $aTemplatesForSave)); + } + + /** + * @return array + * + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoTemplateDelete() + { + $oAccount = $this->getAccountFromToken(); + + if (!$this->GetCapa(false, \RainLoop\Enumerations\Capa::TEMPLATES, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $sId = \trim($this->GetActionParam('IdToDelete', '')); + if (empty($sId)) + { + throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::UnknownError); + } + + $aNew = array(); + $aTemplates = $this->GetTemplates($oAccount); + foreach ($aTemplates as $oItem) + { + if ($oItem && $sId !== $oItem->Id()) + { + $aNew[] = $oItem; + } + } + + return $this->DefaultResponse(__FUNCTION__, $this->SetTemplates($oAccount, $aNew)); + } + + /** + * @return array + * + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoTemplateGetByID() + { + $oAccount = $this->getAccountFromToken(); + + if (!$this->GetCapa(false, \RainLoop\Enumerations\Capa::TEMPLATES, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $sId = \trim($this->GetActionParam('ID', '')); + if (empty($sId)) + { + throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::UnknownError); + } + + $oTemplate = false; + $aTemplates = $this->GetTemplates($oAccount); + + foreach ($aTemplates as $oItem) + { + if ($oItem && $sId === $oItem->Id()) + { + $oTemplate = $oItem; + break; + } + } + + $oTemplate->SetPopulateAlways(true); + return $this->DefaultResponse(__FUNCTION__, $oTemplate); + } + /** * @return array * @@ -2550,6 +2761,25 @@ class Actions )); } + /** + * @return array + * + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoTemplates() + { + $oAccount = $this->getAccountFromToken(); + + if (!$this->GetCapa(false, \RainLoop\Enumerations\Capa::TEMPLATES, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + return $this->DefaultResponse(__FUNCTION__, array( + 'Templates' => $this->GetTemplates($oAccount) + )); + } + /** * @param string $sHash * @@ -2821,6 +3051,9 @@ class Actions case \RainLoop\Enumerations\Capa::ADDITIONAL_ACCOUNTS: $this->setConfigFromParams($oConfig, $sParamName, 'webmail', 'allow_additional_accounts', 'bool'); break; + case \RainLoop\Enumerations\Capa::TEMPLATES: + $this->setConfigFromParams($oConfig, $sParamName, 'capa', 'templates', 'bool'); + break; case \RainLoop\Enumerations\Capa::TWO_FACTOR: $this->setConfigFromParams($oConfig, $sParamName, 'security', 'allow_two_factor_auth', 'bool'); break; @@ -2923,6 +3156,7 @@ class Actions }); $this->setCapaFromParams($oConfig, 'CapaAdditionalAccounts', \RainLoop\Enumerations\Capa::ADDITIONAL_ACCOUNTS); + $this->setCapaFromParams($oConfig, 'CapaTemplates', \RainLoop\Enumerations\Capa::TEMPLATES); $this->setCapaFromParams($oConfig, 'CapaTwoFactorAuth', \RainLoop\Enumerations\Capa::TWO_FACTOR); $this->setCapaFromParams($oConfig, 'CapaOpenPGP', \RainLoop\Enumerations\Capa::OPEN_PGP); $this->setCapaFromParams($oConfig, 'CapaGravatar', \RainLoop\Enumerations\Capa::GRAVATAR); @@ -5303,6 +5537,27 @@ class Actions return $oMessage; } + /** + * @param \RainLoop\Model\Account $oAccount + * + * @return void + */ + private function deleteMessageAttachmnets($oAccount) + { + $aAttachments = $this->GetActionParam('Attachments', null); + + if (\is_array($aAttachments)) + { + foreach (\array_keys($aAttachments) as $sTempName) + { + if ($this->FilesProvider()->FileExists($oAccount, $sTempName)) + { + $this->FilesProvider()->Clear($oAccount, $sTempName); + } + } + } + } + /** * @param \RainLoop\Model\Account $oAccount * @@ -5600,6 +5855,8 @@ class Actions { $this->smtpSendMessage($oAccount, $oMessage, $rMessageStream, $iMessageStreamSize); + $this->deleteMessageAttachmnets($oAccount); + if (is_array($aDraftInfo) && 3 === count($aDraftInfo)) { $sDraftInfoType = $aDraftInfo[0]; @@ -6967,7 +7224,7 @@ class Actions $oSettings = $this->SettingsProvider()->Load($oAccount); if ($oSettings) { - $sHash = \md5($sName.APP_VERSION.APP_SALT.\rand(1000, 9999).\microtime(true)); + $sHash = \MailSo\Base\Utils::Md5Rand($sName.APP_VERSION.APP_SALT); $oSettings->SetConf('UserBackgroundName', $sName); $oSettings->SetConf('UserBackgroundHash', $sHash); @@ -7225,7 +7482,7 @@ class Actions $sLast = \array_pop($aParams); $sUrl = $this->Http()->GetFullUrl().'?/Raw/&s=/'.implode('/', $aParams).'/&ss=/'.$sLast; - $sFullUrl = 'http://docs.google.com/viewer?embedded=true&url='.urlencode($sUrl); + $sFullUrl = 'https://docs.google.com/viewer?embedded=true&url='.urlencode($sUrl); @\header('Content-Type: text/html; charset=utf-8'); echo ''. @@ -7272,6 +7529,8 @@ class Actions $rMessageStream = $this->FilesProvider()->GetFile($oAccount, $sSavedName); $this->MailClient()->MessageAppendStream($rMessageStream, $iMessageStreamSize, $sFolderFullNameRaw); + + $this->FilesProvider()->Clear($oAccount, $sSavedName); } } } @@ -7301,6 +7560,11 @@ class Actions } } +// if ($oConfig->Get('capa', 'templates', true)) +// { +// $aResult[] = \RainLoop\Enumerations\Capa::TEMPLATES; +// } + if ($oConfig->Get('webmail', 'allow_additional_accounts', false)) { $aResult[] = \RainLoop\Enumerations\Capa::ADDITIONAL_ACCOUNTS; @@ -7455,7 +7719,7 @@ class Actions { \MailSo\Base\StreamWrappers\TempFile::Reg(); - $sFileName = 'mailsotempfile://'.\md5($sFileNameOut.\rand(1000, 9999)); + $sFileName = 'mailsotempfile://'.\MailSo\Base\Utils::Md5Rand($sFileNameOut); $rTempResource = \fopen($sFileName, 'r+b'); if (@\is_resource($rTempResource)) @@ -8256,25 +8520,6 @@ class Actions @\header('Location: '.$sUrl); } - /** - * @param string $sTitle - * @param string $sDesc - * - * @return mixed - */ - public function ErrorTemplates($sTitle, $sDesc, $bShowBackLink = true) - { - return strtr(file_get_contents(APP_VERSION_ROOT_PATH.'app/templates/Error.html'), array( - '{{BaseWebStaticPath}}' => APP_WEB_STATIC_PATH, - '{{ErrorTitle}}' => $sTitle, - '{{ErrorHeader}}' => $sTitle, - '{{ErrorDesc}}' => $sDesc, - '{{BackLinkVisibilityStyle}}' => $bShowBackLink ? 'display:inline-block' : 'display:none', - '{{BackLink}}' => $this->StaticI18N('STATIC/BACK_LINK'), - '{{BackHref}}' => './' - )); - } - /** * @param object $oData * @param string $sParent diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Api.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Api.php index 4d2e23cf8..736734b35 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Api.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Api.php @@ -136,7 +136,7 @@ class Api */ public static function GetUserSsoHash($sEmail, $sPassword, $bUseTimeout = true) { - $sSsoHash = \sha1(\rand(10000, 99999).$sEmail.$sPassword.\microtime(true)); + $sSsoHash = \MailSo\Base\Utils::Sha1Rand($sEmail.$sPassword); return \RainLoop\Api::Actions()->Cacher()->Set(\RainLoop\KeyPathHelper::SsoCacherKey($sSsoHash), \RainLoop\Utils::EncodeKeyValues(array( 'Email' => $sEmail, diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Config/Application.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Config/Application.php index 3c9b6bffa..7207daa8a 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Config/Application.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Config/Application.php @@ -129,7 +129,8 @@ class Application extends \RainLoop\Config\AbstractConfig ), 'capa' => array( - 'filters' => array(true) + 'filters' => array(true), + 'templates' => array(true) ), 'login' => array( @@ -164,6 +165,7 @@ Values: 'view_editor_type' => array('Html', 'Editor mode used by default (Plain, Html, HtmlForced or PlainForced)'), 'view_layout' => array(1, 'layout: 0 - no preview, 1 - side preview, 3 - bottom preview'), 'view_use_checkboxes' => array(true), + 'show_images' => array(false), 'contacts_autosave' => array(true), 'mail_use_threads' => array(false), 'mail_reply_same_folder' => array(false) diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Enumerations/Capa.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Enumerations/Capa.php index 5f8dad390..8a5cef54a 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Enumerations/Capa.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Enumerations/Capa.php @@ -15,4 +15,5 @@ class Capa const FILTERS = 'FILTERS'; const ATTACHMENT_THUMBNAILS = 'ATTACHMENT_THUMBNAILS'; const ADDITIONAL_ACCOUNTS = 'ADDITIONAL_ACCOUNTS'; + const TEMPLATES = 'TEMPLATES'; } diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Model/Template.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Model/Template.php new file mode 100644 index 000000000..0d4b1e7b0 --- /dev/null +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Model/Template.php @@ -0,0 +1,149 @@ +sId = $sId; + $this->sName = $sName; + $this->sBody = $sBody; + $this->bPopulateAlways = false; + } + + /** + * @param string $sId = '' + * @param string $sName = '' + * @param string $sBody = '' + * + * @return \RainLoop\Model\Template + */ + public static function NewInstance($sId = '', $sName = '', $sBody = '') + { + return new self($sId, $sBody); + } + + /** + * @return string + */ + public function Id() + { + return $this->sId; + } + + /** + * @return string + */ + public function Name() + { + return $this->sName; + } + + /** + * @return string + */ + public function Body() + { + return $this->sBody; + } + + /** + * @param bool $bPopulateAlways + */ + public function SetPopulateAlways($bPopulateAlways) + { + $this->bPopulateAlways = !!$bPopulateAlways; + + } + + /** + * @param array $aData + * @param bool $bAjax = false + * + * @return bool + */ + public function FromJSON($aData, $bAjax = false) + { + if (isset($aData['ID'], $aData['Name'], $aData['Body'])) + { + $this->sId = $aData['ID']; + $this->sName = $aData['Name']; + $this->sBody = $aData['Body']; + + return true; + } + + return false; + } + + /** + * @param bool $bAjax = false + * + * @return array + */ + public function ToSimpleJSON($bAjax = false) + { + $sBody = $this->Body(); + $bPopulated = true; + + if ($bAjax && $bPopulated && !$this->bPopulateAlways) + { + if (1024 * 5 < \strlen($sBody) || true) + { + $bPopulated = false; + $sBody = ''; + } + } + + return array( + 'ID' => $this->Id(), + 'Name' => $this->Name(), + 'Populated' => $bPopulated, + 'Body' => $sBody + ); + } + + /** + * @return bool + */ + public function GenerateID() + { + return $this->sId = \MailSo\Base\Utils::Md5Rand(); + } + + /** + * @return bool + */ + public function Validate() + { + return 0 < \strlen($this->sBody); + } +} diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php index cfd4916e8..0a20fae41 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php @@ -151,7 +151,7 @@ class Contact public function RegenerateContactStr() { $this->IdContactStr = \class_exists('Sabre\DAV\Client') ? - \Sabre\DAV\UUIDUtil::getUUID() : \md5(\microtime(true).'-'.\rand(10000, 99999)); + \Sabre\DAV\UUIDUtil::getUUID() : \MailSo\Base\Utils::Md5Rand(); } /** diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Files.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Files.php index 750a93a23..638848039 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Files.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Files.php @@ -107,6 +107,15 @@ class Files extends \RainLoop\Providers\AbstractProvider return $this->oDriver ? $this->oDriver->GC($iTimeToClearInHours) : false; } + /** + * @return bool + */ + public function CloseAllOpenedFiles() + { + return $this->oDriver && \method_exists($this->oDriver, 'CloseAllOpenedFiles') ? + $this->oDriver->CloseAllOpenedFiles() : false; + } + /** * @return bool */ diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Files/DefaultStorage.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Files/DefaultStorage.php index d3b7844d2..d377ff248 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Files/DefaultStorage.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Files/DefaultStorage.php @@ -4,6 +4,11 @@ namespace RainLoop\Providers\Files; class DefaultStorage implements \RainLoop\Providers\Files\FilesInterface { + /** + * @var array + */ + private $aResources; + /** * @var string */ @@ -16,6 +21,7 @@ class DefaultStorage implements \RainLoop\Providers\Files\FilesInterface */ public function __construct($sStoragePath) { + $this->aResources = array(); $this->sDataPath = \rtrim(\trim($sStoragePath), '\\/'); } @@ -70,6 +76,11 @@ class DefaultStorage implements \RainLoop\Providers\Files\FilesInterface if ($bCreate || \file_exists($sFileName)) { $mResult = @\fopen($sFileName, $sOpenMode); + + if (\is_resource($mResult)) + { + $this->aResources[$sFileName] = $mResult; + } } return $mResult; @@ -105,6 +116,11 @@ class DefaultStorage implements \RainLoop\Providers\Files\FilesInterface $sFileName = $this->generateFileName($oAccount, $sKey); if (\file_exists($sFileName)) { + if (isset($this->aResources[$sFileName]) && \is_resource($this->aResources[$sFileName])) + { + @\fclose($this->aResources[$sFileName]); + } + $mResult = @\unlink($sFileName); } @@ -156,6 +172,25 @@ class DefaultStorage implements \RainLoop\Providers\Files\FilesInterface return false; } + /** + * @return bool + */ + public function CloseAllOpenedFiles() + { + if (\is_array($this->aResources) && 0 < \count($this->aResources)) + { + foreach ($this->aResources as $sFileName => $rFile) + { + if (!empty($sFileName) && \is_resource($rFile)) + { + @\fclose($rFile); + } + } + } + + return true; + } + /** * @param \RainLoop\Model\Account $oAccount * @param string $sKey diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Service.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Service.php index 944d7c3de..2a8fee58b 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Service.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Service.php @@ -125,7 +125,7 @@ class Service if ($bAdmin && !$this->oActions->Config()->Get('security', 'allow_admin_panel', true)) { - echo $this->oActions->ErrorTemplates('Access Denied.', + echo $this->oServiceActions->ErrorTemplates('Access Denied.', 'Access to the RainLoop Webmail Admin Panel is not allowed!', true); return $this; @@ -150,6 +150,9 @@ class Service if ($bIndex) { + @\header('Content-Security-Policy:'); + @\header_remove('Content-Security-Policy'); + @header('Content-Type: text/html; charset=utf-8'); $this->oHttp->ServerNoCache(); @@ -216,7 +219,7 @@ class Service $bAppJsDebug = !!$this->oActions->Config()->Get('labs', 'use_app_debug_js', false); $bAppCssDebug = !!$this->oActions->Config()->Get('labs', 'use_app_debug_css', false); - $sStaticPrefix = APP_WEB_STATIC_PATH; + $sStaticPrefix = $this->oServiceActions->WebStaticPath(); $aData = array( 'Language' => $sLanguage, @@ -254,7 +257,7 @@ class Service \implode('~', array( \md5($this->oActions->Config()->Get('cache', 'index', '')), $this->oActions->Plugins()->Hash(), - APP_WEB_PATH, APP_VERSION + $this->oServiceActions->WebVersionPath(), APP_VERSION )). \implode('~', $aTemplateParameters) ); diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/ServiceActions.php b/rainloop/v/0.0.0/app/libraries/RainLoop/ServiceActions.php index dc1776a66..bd8f63976 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/ServiceActions.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/ServiceActions.php @@ -675,7 +675,7 @@ class ServiceActions if (\file_exists($sThemeFile) && \file_exists($sThemeTemplateFile) && \file_exists($sThemeValuesFile)) { - $aResult[] = '@base: "'.($bCustomTheme ? '' : APP_WEB_PATH).'themes/'.$sRealTheme.'/";'; + $aResult[] = '@base: "'.($bCustomTheme ? $this->WebPath() : $this->WebVersionPath()).'themes/'.$sRealTheme.'/";'; $aResult[] = \file_get_contents($sThemeValuesFile); $aResult[] = \file_get_contents($sThemeFile); $aResult[] = \file_get_contents($sThemeTemplateFile); @@ -779,7 +779,7 @@ class ServiceActions @\header('Content-Type: text/html; charset=utf-8'); return \strtr(\file_get_contents(APP_VERSION_ROOT_PATH.'app/templates/BadBrowser.html'), array( - '{{BaseWebStaticPath}}' => APP_WEB_STATIC_PATH, + '{{BaseWebStaticPath}}' => $this->WebStaticPath(), '{{ErrorTitle}}' => $sTitle, '{{ErrorHeader}}' => $sTitle, '{{ErrorDesc}}' => $sDesc @@ -1051,6 +1051,58 @@ class ServiceActions return ''; } + /** + * @param string $sTitle + * @param string $sDesc + * + * @return mixed + */ + public function ErrorTemplates($sTitle, $sDesc, $bShowBackLink = true) + { + return strtr(file_get_contents(APP_VERSION_ROOT_PATH.'app/templates/Error.html'), array( + '{{BaseWebStaticPath}}' => $this->WebStaticPath(), + '{{ErrorTitle}}' => $sTitle, + '{{ErrorHeader}}' => $sTitle, + '{{ErrorDesc}}' => $sDesc, + '{{BackLinkVisibilityStyle}}' => $bShowBackLink ? 'display:inline-block' : 'display:none', + '{{BackLink}}' => $this->oActions->StaticI18N('STATIC/BACK_LINK'), + '{{BackHref}}' => './' + )); + } + + /** + * @return string + */ + public function WebPath() + { + if (isset($_ENV['RAINLOOP_OWNCLOUD']) && $_ENV['RAINLOOP_OWNCLOUD']) + { + $sUrl = $this->oHttp->GetUrl(); + if ($sUrl && \preg_match('/\/index\.php\/apps\/rainloop/', $sUrl)) + { + $sUrl = \preg_replace('/\/index\.php\/apps\/rainloop.+$/', '/', $sUrl); + return \rtrim(\trim($sUrl), '\//').'/apps/rainloop/app/'; + } + } + + return ''; + } + /** + * @return string + */ + public function WebVersionPath() + { + return $this->WebPath().'rainloop/v/'.APP_VERSION.'/'; + } + + /** + * @return string + */ + public function WebStaticPath() + { + return $this->WebVersionPath().'static/'; + } + /** * @param string $sTitle * @param string $sDesc diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Utils.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Utils.php index 23a1a516a..f04983e7e 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Utils.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Utils.php @@ -72,7 +72,7 @@ class Utils $sToken = \RainLoop\Utils::GetCookie($sKey, null); if (null === $sToken) { - $sToken = \md5(\rand(10000, 99999).\microtime(true).APP_SALT); + $sToken = \MailSo\Base\Utils::Md5Rand(APP_SALT); \RainLoop\Utils::SetCookie($sKey, $sToken, \time() + 60 * 60 * 24 * 30, '/', null, null, true); } @@ -97,7 +97,7 @@ class Utils $sToken = \RainLoop\Utils::GetCookie($sKey, null); if (null === $sToken) { - $sToken = \md5(\rand(10000, 99999).\microtime(true).APP_SALT); + $sToken = \MailSo\Base\Utils::Md5Rand(APP_SALT); \RainLoop\Utils::SetCookie($sKey, $sToken, 0, '/', null, null, true); } diff --git a/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsGeneral.html b/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsGeneral.html index 1b7699f82..a074aaa6f 100644 --- a/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsGeneral.html +++ b/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsGeneral.html @@ -138,5 +138,18 @@ }"> + \ No newline at end of file diff --git a/rainloop/v/0.0.0/app/templates/Views/User/PopupsTemplate.html b/rainloop/v/0.0.0/app/templates/Views/User/PopupsTemplate.html index ac99ffea9..1bfcc0cff 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/PopupsTemplate.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/PopupsTemplate.html @@ -16,13 +16,15 @@
- - + +
+ +
+
- -
+
diff --git a/rainloop/v/0.0.0/include.php b/rainloop/v/0.0.0/include.php index 22265ac91..17bce383f 100644 --- a/rainloop/v/0.0.0/include.php +++ b/rainloop/v/0.0.0/include.php @@ -26,9 +26,9 @@ Options -Indexes if (file_exists(APP_INDEX_ROOT_PATH.'include.php')) { include_once APP_INDEX_ROOT_PATH.'include.php'; - $sCustomDataPath = function_exists('__get_custom_data_full_path') ? rtrim(trim(__get_custom_data_full_path()), '\\/') : ''; } + $sCustomDataPath = function_exists('__get_custom_data_full_path') ? rtrim(trim(__get_custom_data_full_path()), '\\/') : ''; define('APP_DATA_FOLDER_PATH', 0 === strlen($sCustomDataPath) ? APP_INDEX_ROOT_PATH.'data/' : $sCustomDataPath.'/'); unset($sCustomDataPath); @@ -58,8 +58,6 @@ Options -Indexes define('APP_REP_PATH', 'http://repository.rainloop.net/v1/'); define('APP_REPO_CORE_FILE', 'http://repository.rainloop.net/v2/core.{{channel}}.json'); define('APP_STATUS_PATH', 'http://status.rainloop.net/'); - define('APP_WEB_PATH', 'rainloop/v/'.APP_VERSION.'/'); - define('APP_WEB_STATIC_PATH', APP_WEB_PATH.'static/'); define('APP_DATA_FOLDER_PATH_UNIX', str_replace('\\', '/', APP_DATA_FOLDER_PATH)); $sSalt = @file_get_contents(APP_DATA_FOLDER_PATH.'SALT.php');