From 67c5e31e585045f92552fe7084f02a780b4befe2 Mon Sep 17 00:00:00 2001 From: RainLoop Team Date: Sun, 1 Feb 2015 03:41:15 +0400 Subject: [PATCH] Added a new theme (Rain Drops) Added a "video-on-login-screen" plugin Added a few branding settings --- build/plugin.xml | 5 + dev/App/User.js | 11 +- dev/Settings/Admin/Branding.js | 22 +- dev/Styles/AdminBranding.less | 5 + dev/Styles/Login.less | 2 +- dev/View/User/Login.js | 3 +- gulpfile.js | 6 +- package.json | 2 +- plugins/video-on-login-screen/LICENSE | 20 + plugins/video-on-login-screen/README | 1 + plugins/video-on-login-screen/VERSION | 1 + plugins/video-on-login-screen/index.php | 38 ++ .../js/vide/CHANGELOG.md | 41 ++ .../js/vide/CONTRIBUTING.md | 11 + .../video-on-login-screen/js/vide/MIT-LICENSE | 21 + .../video-on-login-screen/js/vide/README.md | 140 ++++++ .../video-on-login-screen/js/vide/bower.json | 40 ++ .../js/vide/jquery.vide.js | 442 ++++++++++++++++++ .../js/vide/jquery.vide.min.js | 9 + .../js/video-on-login.js | 19 + .../0.0.0/app/libraries/RainLoop/Actions.php | 21 +- .../libraries/RainLoop/Config/Application.php | 4 +- rainloop/v/0.0.0/app/templates/Index.html | 21 +- .../Views/Admin/AdminSettingsBranding.html | 45 +- .../0.0.0/app/templates/Views/User/Login.html | 1 - .../themes/RainDrops/images/background.jpg | Bin 0 -> 198151 bytes .../0.0.0/themes/RainDrops/images/preview.png | Bin 0 -> 7048 bytes rainloop/v/0.0.0/themes/RainDrops/styles.less | 58 +++ vendors/rl/LICENSE | 2 +- vendors/rl/rl-1.4.min.js | 2 - vendors/rl/rl-1.5.min.js | 2 + vendors/rl/rl.js | 6 +- 32 files changed, 971 insertions(+), 30 deletions(-) create mode 100644 plugins/video-on-login-screen/LICENSE create mode 100644 plugins/video-on-login-screen/README create mode 100644 plugins/video-on-login-screen/VERSION create mode 100644 plugins/video-on-login-screen/index.php create mode 100644 plugins/video-on-login-screen/js/vide/CHANGELOG.md create mode 100644 plugins/video-on-login-screen/js/vide/CONTRIBUTING.md create mode 100644 plugins/video-on-login-screen/js/vide/MIT-LICENSE create mode 100644 plugins/video-on-login-screen/js/vide/README.md create mode 100644 plugins/video-on-login-screen/js/vide/bower.json create mode 100644 plugins/video-on-login-screen/js/vide/jquery.vide.js create mode 100644 plugins/video-on-login-screen/js/vide/jquery.vide.min.js create mode 100644 plugins/video-on-login-screen/js/video-on-login.js create mode 100644 rainloop/v/0.0.0/themes/RainDrops/images/background.jpg create mode 100644 rainloop/v/0.0.0/themes/RainDrops/images/preview.png create mode 100644 rainloop/v/0.0.0/themes/RainDrops/styles.less delete mode 100644 vendors/rl/rl-1.4.min.js create mode 100644 vendors/rl/rl-1.5.min.js diff --git a/build/plugin.xml b/build/plugin.xml index 5ee0cb685..8afe74ea8 100644 --- a/build/plugin.xml +++ b/build/plugin.xml @@ -103,5 +103,10 @@ + + + + + diff --git a/dev/App/User.js b/dev/App/User.js index af86fc5bf..332884a3a 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -122,10 +122,11 @@ if (Settings.settingsGet('UserBackgroundHash')) { _.delay(function () { - $('#rl-bg').backstretch(Links.publicLink(Settings.settingsGet('UserBackgroundHash')), { + $('#rl-bg').attr('style', 'background-image: none !important;') + .backstretch(Links.publicLink(Settings.settingsGet('UserBackgroundHash')), { 'fade': Globals.bAnimationSupported ? 1000 : 0 - }); - }, 3000); + }).removeAttr('style'); + }, 2000); } this.socialUsers = _.bind(this.socialUsers, this); @@ -1354,6 +1355,8 @@ AppUser.prototype.bootstartLoginScreen = function () { + Globals.$html.removeClass('rl-user-auth').addClass('rl-user-no-auth'); + var sCustomLoginLink = Utils.pString(Settings.settingsGet('CustomLoginLink')); if (!sCustomLoginLink) { @@ -1411,6 +1414,8 @@ if (!!Settings.settingsGet('Auth')) { + Globals.$html.addClass('rl-user-auth'); + this.setTitle(Translator.i18n('TITLES/LOADING')); //require.ensure([], function() { // require code splitting diff --git a/dev/Settings/Admin/Branding.js b/dev/Settings/Admin/Branding.js index ec2636e7b..32e92da42 100644 --- a/dev/Settings/Admin/Branding.js +++ b/dev/Settings/Admin/Branding.js @@ -31,6 +31,9 @@ this.loginLogo = ko.observable(Settings.settingsGet('LoginLogo') || ''); this.loginLogo.trigger = ko.observable(Enums.SaveSettingsStep.Idle); + this.loginBackground = ko.observable(Settings.settingsGet('LoginBackground') || ''); + this.loginBackground.trigger = ko.observable(Enums.SaveSettingsStep.Idle); + this.userLogo = ko.observable(Settings.settingsGet('UserLogo') || ''); this.userLogo.trigger = ko.observable(Enums.SaveSettingsStep.Idle); @@ -40,6 +43,9 @@ this.loginCss = ko.observable(Settings.settingsGet('LoginCss')); this.loginCss.trigger = ko.observable(Enums.SaveSettingsStep.Idle); + this.userCss = ko.observable(Settings.settingsGet('UserCss')); + this.userCss.trigger = ko.observable(Enums.SaveSettingsStep.Idle); + this.loginPowered = ko.observable(!!Settings.settingsGet('LoginPowered')); } @@ -60,7 +66,9 @@ f3 = Utils.settingsSaveHelperSimpleFunction(self.loginLogo.trigger, self), f4 = Utils.settingsSaveHelperSimpleFunction(self.loginDescription.trigger, self), f5 = Utils.settingsSaveHelperSimpleFunction(self.loginCss.trigger, self), - f6 = Utils.settingsSaveHelperSimpleFunction(self.userLogo.trigger, self) + f6 = Utils.settingsSaveHelperSimpleFunction(self.userLogo.trigger, self), + f7 = Utils.settingsSaveHelperSimpleFunction(self.loginBackground.trigger, self), + f8 = Utils.settingsSaveHelperSimpleFunction(self.userCss.trigger, self) ; self.title.subscribe(function (sValue) { @@ -81,6 +89,12 @@ }); }); + self.loginBackground.subscribe(function (sValue) { + Remote.saveAdminConfig(f7, { + 'LoginBackground': Utils.trim(sValue) + }); + }); + self.userLogo.subscribe(function (sValue) { Remote.saveAdminConfig(f6, { 'UserLogo': Utils.trim(sValue) @@ -99,6 +113,12 @@ }); }); + self.userCss.subscribe(function (sValue) { + Remote.saveAdminConfig(f8, { + 'UserCss': Utils.trim(sValue) + }); + }); + self.loginPowered.subscribe(function (bValue) { Remote.saveAdminConfig(null, { 'LoginPowered': bValue ? '1' : '0' diff --git a/dev/Styles/AdminBranding.less b/dev/Styles/AdminBranding.less index 8a8db695c..ccad55da7 100644 --- a/dev/Styles/AdminBranding.less +++ b/dev/Styles/AdminBranding.less @@ -1,7 +1,12 @@ .b-admin-branding { + .disabled-form { opacity: 0.8; } + + .custom-css-wrapper textarea { + font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; + } } diff --git a/dev/Styles/Login.less b/dev/Styles/Login.less index 628906ec9..0fc1d0095 100644 --- a/dev/Styles/Login.less +++ b/dev/Styles/Login.less @@ -43,7 +43,7 @@ } .wrapper { - padding: 40px 40px 10px 40px; + padding: 40px 40px 12px 40px; } #recaptcha_image img { diff --git a/dev/View/User/Login.js b/dev/View/User/Login.js index 39ed38b48..e40aea8e6 100644 --- a/dev/View/User/Login.js +++ b/dev/View/User/Login.js @@ -47,9 +47,8 @@ this.additionalCodeSignMe = ko.observable(false); this.logoImg = Utils.trim(Settings.settingsGet('LoginLogo')); - this.loginDescription = Utils.trim(Settings.settingsGet('LoginDescription')); - this.logoCss = Utils.trim(Settings.settingsGet('LoginCss')); this.logoPowered = !!Settings.settingsGet('LoginPowered'); + this.loginDescription = Utils.trim(Settings.settingsGet('LoginDescription')); this.forgotPasswordLinkUrl = Settings.settingsGet('ForgotPasswordLinkUrl'); this.registrationLinkUrl = Settings.settingsGet('RegistrationLinkUrl'); diff --git a/gulpfile.js b/gulpfile.js index accb4cc99..41ca2c310 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -113,7 +113,7 @@ cfg.paths.js = { 'vendors/json2.min.js', 'vendors/labjs/LAB.min.js', 'vendors/simple-pace/simple-pace-1.0.min.js', - 'vendors/rl/rl-1.4.min.js' + 'vendors/rl/rl-1.5.min.js' ] }, openpgp: { @@ -387,8 +387,8 @@ regOtherMinTask('other:inputosaurus', 'vendors/inputosaurus/', 'inputosaurus.js' regOtherMinTask('other:pace', 'vendors/simple-pace/', 'simple-pace.js', 'simple-pace-1.0.min.js', '/*! RainLoop Simple Pace v1.0 (c) 2014 RainLoop Team; Licensed under MIT */\n'); -regOtherMinTask('other:rl', 'vendors/rl/', 'rl.js', 'rl-1.4.min.js', - '/*! RainLoop Index Helper v1.4 (c) 2014 RainLoop Team; Licensed under MIT */\n'); +regOtherMinTask('other:rl', 'vendors/rl/', 'rl.js', 'rl-1.5.min.js', + '/*! RainLoop Index Helper v1.5 (c) 2015 RainLoop Team; Licensed under MIT */\n'); gulp.task('fontastic-fonts:clear', function() { return cleanDir('rainloop/v/' + cfg.devVersion + '/static/css/fonts/rainloop.*'); diff --git a/package.json b/package.json index f3ff02a91..6be243090 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "RainLoop", "title": "RainLoop Webmail", "version": "1.7.3", - "release": "237", + "release": "240", "description": "Simple, modern & fast web-based email client", "homepage": "http://rainloop.net", "main": "gulpfile.js", diff --git a/plugins/video-on-login-screen/LICENSE b/plugins/video-on-login-screen/LICENSE new file mode 100644 index 000000000..4aed64b3a --- /dev/null +++ b/plugins/video-on-login-screen/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2015 RainLoop Team + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/plugins/video-on-login-screen/README b/plugins/video-on-login-screen/README new file mode 100644 index 000000000..ec8c5bbdb --- /dev/null +++ b/plugins/video-on-login-screen/README @@ -0,0 +1 @@ +Fullscreen background video on login screen. \ No newline at end of file diff --git a/plugins/video-on-login-screen/VERSION b/plugins/video-on-login-screen/VERSION new file mode 100644 index 000000000..9f8e9b69a --- /dev/null +++ b/plugins/video-on-login-screen/VERSION @@ -0,0 +1 @@ +1.0 \ No newline at end of file diff --git a/plugins/video-on-login-screen/index.php b/plugins/video-on-login-screen/index.php new file mode 100644 index 000000000..ad4c51b45 --- /dev/null +++ b/plugins/video-on-login-screen/index.php @@ -0,0 +1,38 @@ +addJs('js/vide/jquery.vide.js'); + $this->addJs('js/video-on-login.js'); + } + + /** + * @return array + */ + public function configMapping() + { + return array( + \RainLoop\Plugins\Property::NewInstance('mp4_file')->SetLabel('Url to a mp4 file') + ->SetPlaceholder('http://') + ->SetAllowedInJs(true) + ->SetDefaultValue(''), + \RainLoop\Plugins\Property::NewInstance('webm_file')->SetLabel('Url to a webm file') + ->SetPlaceholder('http://') + ->SetAllowedInJs(true) + ->SetDefaultValue(''), + \RainLoop\Plugins\Property::NewInstance('ogv_file')->SetLabel('Url to a ogv file') + ->SetPlaceholder('http://') + ->SetAllowedInJs(true) + ->SetDefaultValue(''), + \RainLoop\Plugins\Property::NewInstance('muted')->SetLabel('Muted') + ->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL) + ->SetAllowedInJs(true) + ->SetDefaultValue(true), + ); + } +} diff --git a/plugins/video-on-login-screen/js/vide/CHANGELOG.md b/plugins/video-on-login-screen/js/vide/CHANGELOG.md new file mode 100644 index 000000000..c922ebbd8 --- /dev/null +++ b/plugins/video-on-login-screen/js/vide/CHANGELOG.md @@ -0,0 +1,41 @@ +### 0.3.0 +* Added the `resizing` option. +* Updated tests. + +### 0.2.1 +* Code refactoring. +* Updated devDependencies. + +### 0.2.0 +* Lots of updates since 0.1.0. +* Improved code linting. +* Cleaned up the repository. +* Added bower dependencies. +* Updated devDependencies. + +### 0.1.4 +* Fixed wrong URL parsing. +* Changed main files in bower.json. + +### 0.1.3 +* Path argument can receive list of sources. +* Strings with options and pathes can be passed to the constructor directly. +* Added `posterType: none` value. +* Updated README. +* Updated JSDoc. +* Updated tests. +* Updated examples. +* Added CONTRIBUTING.md +* Other small fixes and optimizations. + +### 0.1.2 +* Restored `posterType` option to specify poster image type. + +### 0.1.1 +* Support of absolute URLs (#10). +* Fixed the CORS issue (#11). +* Fixed the destroy method. +* Fixed parsing of empty options. +* Poster and video positions are the same now. +* Syntax and behavior of the position option are similar to the CSS `background-position` property. +* Add support of the `.jpeg` extension. diff --git a/plugins/video-on-login-screen/js/vide/CONTRIBUTING.md b/plugins/video-on-login-screen/js/vide/CONTRIBUTING.md new file mode 100644 index 000000000..3900d1fe7 --- /dev/null +++ b/plugins/video-on-login-screen/js/vide/CONTRIBUTING.md @@ -0,0 +1,11 @@ +CONTRIBUTING +==== + +1. Fork. +2. Run `npm install`. +3. Make your changes on the `src` folder. +4. Update tests. +5. Run `npm test`, make sure everything is okay. +6. Submit a pull request to the master branch. + +Thanks. diff --git a/plugins/video-on-login-screen/js/vide/MIT-LICENSE b/plugins/video-on-login-screen/js/vide/MIT-LICENSE new file mode 100644 index 000000000..f03343d6d --- /dev/null +++ b/plugins/video-on-login-screen/js/vide/MIT-LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Ilya Makarov, http://vodkabears.github.io + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/plugins/video-on-login-screen/js/vide/README.md b/plugins/video-on-login-screen/js/vide/README.md new file mode 100644 index 000000000..546d81975 --- /dev/null +++ b/plugins/video-on-login-screen/js/vide/README.md @@ -0,0 +1,140 @@ +[![Bower version](https://badge.fury.io/bo/vide.svg)](http://badge.fury.io/bo/vide) +[![Travis](https://travis-ci.org/VodkaBears/Vide.svg?branch=master)](https://travis-ci.org/VodkaBears/Vide) +[![devDependency Status](https://david-dm.org/vodkabears/vide/dev-status.svg)](https://david-dm.org/vodkabears/vide#info=devDependencies) +Vide +==== + +Easy as hell jQuery plugin for video backgrounds. + +## Notes + +* All modern desktop browsers are supported. +* IE9+ +* iOS plays video from a browser only in the native player. So video for iOS is disabled, only fullscreen poster will be used. +* Some android devices play video, some not — go figure. So video for android is disabled, only fullscreen poster will be used. + +## Instructions + +Download it from [GitHub](https://github.com/VodkaBears/Vide/releases/latest) or via Bower: +`bower install vide` + +Include plugin: `` + +Prepare your video in several formats like '.webm', '.mp4' for cross browser compatability, also add a poster with `.jpg`, `.png` or `.gif` extension: +``` +path/ +├── to/ +│ ├── video.mp4 +│ ├── video.ogv +│ ├── video.webm +│ └── video.jpg +``` + +Add `data-vide-bg` attribute with a path to the video and poster without extension, video and poster must have the same name. Add `data-vide-options` to pass vide options, if you need it. By default video is muted, looped and starts automatically. +```html +
+
+``` + +Also you can set extended path: +```html +
+
+``` + +In some situations it can be helpful to initialize it with JS because Vide doesn't have mutation observers: +```js +$("#myBlock").vide("path/to/video"); +$("#myBlock").vide("path/to/video", { +...options... +}); +$("#myBlock").vide({ + mp4: path/to/video1, + webm: path/to/video2, + ogv: path/to/video3, + poster: path/to/poster +}, { +...options... +}); +$("#myBlock").vide("extended path as a string", "options as a string"); +``` + +Easy as hell. + +## Options + +Below is a complete list of options and matching default values: + +```js +$("#yourElement").vide({ + volume: 1, + playbackRate: 1, + muted: true, + loop: true, + autoplay: true, + position: "50% 50%", // Similar to the CSS `background-position` property. + posterType: "detect", // Poster image type. "detect" — auto-detection; "none" — no poster; "jpg", "png", "gif",... - extensions. + resizing: true // Auto-resizing, read: https://github.com/VodkaBears/Vide#resizing +}); +``` + +## Methods + +Below is a complete list of methods: + +```js +// Get instance of the plugin +var instance = $("#yourElement").data("vide"); + +// Get video element of the background. Do what you want. +instance.getVideoObject(); + +// Resize video background. +// It calls automatically, if window resize (or element, if you will use something like https://github.com/cowboy/jquery-resize). +instance.resize(); + +// Destroy plugin instance +instance.destroy(); +``` + +## Resizing + +The Vide plugin resizes if the window resizes. If you will use something like https://github.com/cowboy/jquery-resize, it will resize automatically when the container resizes. Or simply use `resize()` method whenever you need. + +Set the `resizing` option to false to disable auto-resizing. + +## Encoding video + +http://diveintohtml5.info/video.html#miro + +## Ruby Gem + +[Vider](https://github.com/wazery/vider) by Islam Wazery. + +## License + +``` +The MIT License (MIT) + +Copyright (c) 2014 Ilya Makarov, http://vodkabears.github.io + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` diff --git a/plugins/video-on-login-screen/js/vide/bower.json b/plugins/video-on-login-screen/js/vide/bower.json new file mode 100644 index 000000000..7d4c47379 --- /dev/null +++ b/plugins/video-on-login-screen/js/vide/bower.json @@ -0,0 +1,40 @@ +{ + "name": "vide", + "version": "0.3.0", + "homepage": "http://vodkabears.github.io/vide/", + "authors": [ + "Ilya Makarov " + ], + "description": "Easy as hell jQuery plugin for video backgrounds.", + "main": "dist/jquery.vide.js", + "ignore": [ + "**/.*", + "examples/", + "libs/", + "src/", + "test/", + "*.md", + "Gruntfile.js", + "package.json", + "vide.jquery.json" + ], + "keywords": [ + "jquery", + "plugin", + "jquery-plugin", + "video", + "background", + "ui", + "responsive", + "declarative" + ], + "license": "MIT", + "dependencies": { + "jquery": "*" + }, + "devDependencies": { + "qunit": "~1.15.0", + "jquery": "jquery#^1.11.1", + "jquery2": "jquery#^2.1.1" + } +} diff --git a/plugins/video-on-login-screen/js/vide/jquery.vide.js b/plugins/video-on-login-screen/js/vide/jquery.vide.js new file mode 100644 index 000000000..cc5f29dfc --- /dev/null +++ b/plugins/video-on-login-screen/js/vide/jquery.vide.js @@ -0,0 +1,442 @@ +/* + * Vide - v0.3.0 + * Easy as hell jQuery plugin for video backgrounds. + * http://vodkabears.github.io/vide/ + * + * Made by Ilya Makarov + * Under MIT License + */ +!(function($, window, document, navigator) { + "use strict"; + + /** + * Vide settings + * @private + */ + var pluginName = "vide", + defaults = { + volume: 1, + playbackRate: 1, + muted: true, + loop: true, + autoplay: true, + position: "50% 50%", + posterType: "detect", + resizing: true + }, + + // is iOs? + isIOS = /iPad|iPhone|iPod/i.test(navigator.userAgent), + + // is Android? + isAndroid = /Android/i.test(navigator.userAgent); + + /** + * Parse string with options + * @param {String} str + * @returns {Object|String} + * @private + */ + function parseOptions(str) { + var obj = {}, + delimiterIndex, + option, + prop, val, + arr, len, + i; + + // remove spaces around delimiters and split + arr = str.replace(/\s*:\s*/g, ":").replace(/\s*,\s*/g, ",").split(","); + + // parse string + for (i = 0, len = arr.length; i < len; i++) { + option = arr[i]; + + // Ignore urls and string without colon delimiters + if (option.search(/^(http|https|ftp):\/\//) !== -1 || + option.search(":") === -1) + { + break; + } + + delimiterIndex = option.indexOf(":"); + prop = option.substring(0, delimiterIndex); + val = option.substring(delimiterIndex + 1); + + // if val is an empty string, make it undefined + if (!val) { + val = undefined; + } + + // convert string value if it is like a boolean + if (typeof val === "string") { + val = val === "true" || (val === "false" ? false : val); + } + + // convert string value if it is like a number + if (typeof val === "string") { + val = !isNaN(val) ? +val : val; + } + + obj[prop] = val; + } + + // if nothing is parsed + if (prop == null && val == null) { + return str; + } + + return obj; + } + + /** + * Parse position option + * @param {String} str + * @returns {Object} + * @private + */ + function parsePosition(str) { + str = "" + str; + + // default value is a center + var args = str.split(/\s+/), + x = "50%", y = "50%", + len, arg, + i; + + for (i = 0, len = args.length; i < len; i++) { + arg = args[i]; + + // convert values + if (arg === "left") { + x = "0%"; + } else if (arg === "right") { + x = "100%"; + } else if (arg === "top") { + y = "0%"; + } else if (arg === "bottom") { + y = "100%"; + } else if (arg === "center") { + if (i === 0) { + x = "50%"; + } else { + y = "50%"; + } + } else { + if (i === 0) { + x = arg; + } else { + y = arg; + } + } + } + + return { x: x, y: y }; + } + + /** + * Search poster + * @param {String} path + * @param {Function} callback + * @private + */ + function findPoster(path, callback) { + var onLoad = function() { + callback(this.src); + }; + + $("").load(onLoad); + $("").load(onLoad); + $("").load(onLoad); + $("").load(onLoad); + } + + /** + * Vide constructor + * @param {HTMLElement} element + * @param {Object|String} path + * @param {Object|String} options + * @constructor + */ + function Vide(element, path, options) { + this.$element = $(element); + + // parse path + if (typeof path === "string") { + path = parseOptions(path); + } + + // parse options + if (!options) { + options = {}; + } else if (typeof options === "string") { + options = parseOptions(options); + } + + // remove extension + if (typeof path === "string") { + path = path.replace(/\.\w*$/, ""); + } else if (typeof path === "object") { + for (var i in path) { + if (path.hasOwnProperty(i)) { + path[i] = path[i].replace(/\.\w*$/, ""); + } + } + } + + this.settings = $.extend({}, defaults, options); + this.path = path; + + this.init(); + } + + /** + * Initialization + * @public + */ + Vide.prototype.init = function() { + var vide = this, + position = parsePosition(vide.settings.position), + sources, + poster; + + // Set video wrapper styles + vide.$wrapper = $("
").css({ + "position": "absolute", + "z-index": -1, + "top": 0, + "left": 0, + "bottom": 0, + "right": 0, + "overflow": "hidden", + "-webkit-background-size": "cover", + "-moz-background-size": "cover", + "-o-background-size": "cover", + "background-size": "cover", + "background-repeat": "no-repeat", + "background-position": position.x + " " + position.y + }); + + // Get poster path + poster = vide.path; + if (typeof vide.path === "object") { + if (vide.path.poster) { + poster = vide.path.poster; + } else { + if (vide.path.mp4) { + poster = vide.path.mp4; + } else if (vide.path.webm) { + poster = vide.path.webm; + } else if (vide.path.ogv) { + poster = vide.path.ogv; + } + } + } + + // Set video poster +// if (vide.settings.posterType === "detect") { +// findPoster(poster, function(url) { +// vide.$wrapper.css("background-image", "url(" + url + ")"); +// }); +// } else if (vide.settings.posterType !== "none") { +// vide.$wrapper +// .css("background-image", "url(" + poster + "." + vide.settings.posterType + ")"); +// } + + // if parent element has a static position, make it relative + if (vide.$element.css("position") === "static") { + vide.$element.css("position", "relative"); + } + + vide.$element.prepend(vide.$wrapper); + + if (!isIOS && !isAndroid) { + sources = ""; + + if (typeof vide.path === "object") { + if (vide.path.mp4) { + sources += ""; + } + if (vide.path.webm) { + sources += ""; + } + if (vide.path.ogv) { + sources += ""; + } + + vide.$video = $(""); + } else { + vide.$video = $(""); + } + + // Disable visibility, while loading + vide.$video.css("visibility", "hidden"); + + // Set video properties + vide.$video.prop({ + autoplay: vide.settings.autoplay, + loop: vide.settings.loop, + volume: vide.settings.volume, + muted: vide.settings.muted, + playbackRate: vide.settings.playbackRate + }); + + // Append video + vide.$wrapper.append(vide.$video); + + // Video alignment + vide.$video.css({ + "margin": "auto", + "position": "absolute", + "z-index": -1, + "top": position.y, + "left": position.x, + "-webkit-transform": "translate(-" + position.x + ", -" + position.y + ")", + "-ms-transform": "translate(-" + position.x + ", -" + position.y + ")", + "transform": "translate(-" + position.x + ", -" + position.y + ")" + }); + + // resize video, when it's loaded + vide.$video.bind("loadedmetadata." + pluginName, function() { + vide.$video.css("visibility", "visible"); + vide.resize(); + vide.$wrapper.css("background-image", "none"); + }); + + // resize event is available only for 'window', + // use another code solutions to detect DOM elements resizing + vide.$element.bind("resize." + pluginName, function() { + if (vide.settings.resizing) { + vide.resize(); + } + }); + } + }; + + /** + * Get video element of the background + * @returns {HTMLVideoElement|null} + * @public + */ + Vide.prototype.getVideoObject = function() { + return this.$video ? this.$video[0] : null; + }; + + /** + * Resize video background + * @public + */ + Vide.prototype.resize = function() { + if (!this.$video) { + return; + } + + var + // get native video size + videoHeight = this.$video[0].videoHeight, + videoWidth = this.$video[0].videoWidth, + + // get wrapper size + wrapperHeight = this.$wrapper.height(), + wrapperWidth = this.$wrapper.width(); + + if (wrapperWidth / videoWidth > wrapperHeight / videoHeight) { + this.$video.css({ + "width": wrapperWidth + 2, + + // +2 pixels to prevent empty space after transformation + "height": "auto" + }); + } else { + this.$video.css({ + "width": "auto", + + // +2 pixels to prevent empty space after transformation + "height": wrapperHeight + 2 + }); + } + }; + + /** + * Destroy video background + * @public + */ + Vide.prototype.destroy = function() { + this.$element.unbind(pluginName); + + if (this.$video) { + this.$video.unbind(pluginName); + } + + delete $[pluginName].lookup[this.index]; + this.$element.removeData(pluginName); + this.$wrapper.remove(); + }; + + /** + * Special plugin object for instances. + * @type {Object} + * @public + */ + $[pluginName] = { + lookup: [] + }; + + /** + * Plugin constructor + * @param {Object|String} path + * @param {Object|String} options + * @returns {JQuery} + * @constructor + */ + $.fn[pluginName] = function(path, options) { + var instance; + + this.each(function() { + instance = $.data(this, pluginName); + + if (instance) { + + // destroy plugin instance if exists + instance.destroy(); + } + + // create plugin instance + instance = new Vide(this, path, options); + instance.index = $[pluginName].lookup.push(instance) - 1; + $.data(this, pluginName, instance); + }); + + return this; + }; + + $(document).ready(function() { + + // window resize event listener + $(window).bind("resize." + pluginName, function() { + for (var len = $[pluginName].lookup.length, i = 0, instance; i < len; i++) { + instance = $[pluginName].lookup[i]; + + if (instance && instance.settings.resizing) { + instance.resize(); + } + } + }); + + // Auto initialization. + // Add 'data-vide-bg' attribute with a path to the video without extension. + // Also you can pass options throw the 'data-vide-options' attribute. + // 'data-vide-options' must be like "muted: false, volume: 0.5". + $(document).find("[data-" + pluginName + "-bg]").each(function(i, element) { + var $element = $(element), + options = $element.data(pluginName + "-options"), + path = $element.data(pluginName + "-bg"); + + $element[pluginName](path, options); + }); + }); +})(window.jQuery, window, document, navigator); diff --git a/plugins/video-on-login-screen/js/vide/jquery.vide.min.js b/plugins/video-on-login-screen/js/vide/jquery.vide.min.js new file mode 100644 index 000000000..20f3182cb --- /dev/null +++ b/plugins/video-on-login-screen/js/vide/jquery.vide.min.js @@ -0,0 +1,9 @@ +/* + * Vide - v0.3.0 + * Easy as hell jQuery plugin for video backgrounds. + * http://vodkabears.github.io/vide/ + * + * Made by Ilya Makarov + * Under MIT License + */ +!function(a,b,c,d){"use strict";function e(a){var b,c,d,e,f,g,h,i={};for(f=a.replace(/\s*:\s*/g,":").replace(/\s*,\s*/g,",").split(","),h=0,g=f.length;g>h&&(c=f[h],-1===c.search(/^(http|https|ftp):\/\//)&&-1!==c.search(":"));h++)b=c.indexOf(":"),d=c.substring(0,b),e=c.substring(b+1),e||(e=void 0),"string"==typeof e&&(e="true"===e||("false"===e?!1:e)),"string"==typeof e&&(e=isNaN(e)?e:+e),i[d]=e;return null==d&&null==e?a:i}function f(a){a=""+a;var b,c,d,e=a.split(/\s+/),f="50%",g="50%";for(d=0,b=e.length;b>d;d++)c=e[d],"left"===c?f="0%":"right"===c?f="100%":"top"===c?g="0%":"bottom"===c?g="100%":"center"===c?0===d?f="50%":g="50%":0===d?f=c:g=c;return{x:f,y:g}}function g(b,c){var d=function(){c(this.src)};a("").load(d),a("").load(d),a("").load(d),a("").load(d)}function h(b,c,d){if(this.$element=a(b),"string"==typeof c&&(c=e(c)),d?"string"==typeof d&&(d=e(d)):d={},"string"==typeof c)c=c.replace(/\.\w*$/,"");else if("object"==typeof c)for(var f in c)c.hasOwnProperty(f)&&(c[f]=c[f].replace(/\.\w*$/,""));this.settings=a.extend({},j,d),this.path=c,this.init()}var i="vide",j={volume:1,playbackRate:1,muted:!0,loop:!0,autoplay:!0,position:"50% 50%",posterType:"detect",resizing:!0},k=/iPad|iPhone|iPod/i.test(d.userAgent),l=/Android/i.test(d.userAgent);h.prototype.init=function(){var b,c,d=this,e=f(d.settings.position);d.$wrapper=a("
").css({position:"absolute","z-index":-1,top:0,left:0,bottom:0,right:0,overflow:"hidden","-webkit-background-size":"cover","-moz-background-size":"cover","-o-background-size":"cover","background-size":"cover","background-repeat":"no-repeat","background-position":e.x+" "+e.y}),c=d.path,"object"==typeof d.path&&(d.path.poster?c=d.path.poster:d.path.mp4?c=d.path.mp4:d.path.webm?c=d.path.webm:d.path.ogv&&(c=d.path.ogv)),"detect"===d.settings.posterType?g(c,function(a){d.$wrapper.css("background-image","url("+a+")")}):"none"!==d.settings.posterType&&d.$wrapper.css("background-image","url("+c+"."+d.settings.posterType+")"),"static"===d.$element.css("position")&&d.$element.css("position","relative"),d.$element.prepend(d.$wrapper),k||l||(b="","object"==typeof d.path?(d.path.mp4&&(b+=""),d.path.webm&&(b+=""),d.path.ogv&&(b+=""),d.$video=a("")):d.$video=a(""),d.$video.css("visibility","hidden"),d.$video.prop({autoplay:d.settings.autoplay,loop:d.settings.loop,volume:d.settings.volume,muted:d.settings.muted,playbackRate:d.settings.playbackRate}),d.$wrapper.append(d.$video),d.$video.css({margin:"auto",position:"absolute","z-index":-1,top:e.y,left:e.x,"-webkit-transform":"translate(-"+e.x+", -"+e.y+")","-ms-transform":"translate(-"+e.x+", -"+e.y+")",transform:"translate(-"+e.x+", -"+e.y+")"}),d.$video.bind("loadedmetadata."+i,function(){d.$video.css("visibility","visible"),d.resize(),d.$wrapper.css("background-image","none")}),d.$element.bind("resize."+i,function(){d.settings.resizing&&d.resize()}))},h.prototype.getVideoObject=function(){return this.$video?this.$video[0]:null},h.prototype.resize=function(){if(this.$video){var a=this.$video[0].videoHeight,b=this.$video[0].videoWidth,c=this.$wrapper.height(),d=this.$wrapper.width();this.$video.css(d/b>c/a?{width:d+2,height:"auto"}:{width:"auto",height:c+2})}},h.prototype.destroy=function(){this.$element.unbind(i),this.$video&&this.$video.unbind(i),delete a[i].lookup[this.index],this.$element.removeData(i),this.$wrapper.remove()},a[i]={lookup:[]},a.fn[i]=function(b,c){var d;return this.each(function(){d=a.data(this,i),d&&d.destroy(),d=new h(this,b,c),d.index=a[i].lookup.push(d)-1,a.data(this,i,d)}),this},a(c).ready(function(){a(b).bind("resize."+i,function(){for(var b,c=a[i].lookup.length,d=0;c>d;d++)b=a[i].lookup[d],b&&b.settings.resizing&&b.resize()}),a(c).find("[data-"+i+"-bg]").each(function(b,c){var d=a(c),e=d.data(i+"-options"),f=d.data(i+"-bg");d[i](f,e)})})}(window.jQuery,window,document,navigator); \ No newline at end of file diff --git a/plugins/video-on-login-screen/js/video-on-login.js b/plugins/video-on-login-screen/js/video-on-login.js new file mode 100644 index 000000000..06a7cfb3a --- /dev/null +++ b/plugins/video-on-login-screen/js/video-on-login.js @@ -0,0 +1,19 @@ +(function ($, window) { + + $(function () { + + if (window.rl && window.rl && !window.rl.settingsGet('Auth')) + { + $('#rl-bg').vide({ + 'mp4': window.rl.pluginSettingsGet('video-on-login-screen', 'mp4_file') || '', + 'webm': window.rl.pluginSettingsGet('video-on-login-screen', 'webm_file') || '', + 'ogv': window.rl.pluginSettingsGet('video-on-login-screen', 'ogv_file') || '' + }, { + muted: !!window.rl.pluginSettingsGet('video-on-login-screen', 'muted') + }); + } + + }); + +}($, window)); + 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 63513ca13..e052ee287 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php @@ -1058,11 +1058,15 @@ class Actions 'Title' => 'RainLoop Webmail', 'LoadingDescription' => 'RainLoop', 'LoadingDescriptionEsc' => 'RainLoop', - 'LoginLogo' => '', 'LoginDescription' => '', - 'LoginCss' => '', 'LoginPowered' => true, + 'LoginLogo' => '', + 'LoginBackground' => '', + 'LoginCss' => '', 'UserLogo' => '', + 'UserCss' => '', + 'IncludeCss' => '', + 'IncludeBackground' => '', 'Token' => $oConfig->Get('security', 'csrf_protection', false) ? \RainLoop\Utils::GetCsrfToken() : '', 'InIframe' => (bool) $oConfig->Get('labs', 'in_iframe', false), 'AllowAdminPanel' => (bool) $oConfig->Get('security', 'allow_admin_panel', true), @@ -1120,10 +1124,12 @@ class Actions $aResult['LoadingDescription'] = $oConfig->Get('webmail', 'loading_description', ''); $aResult['LoginLogo'] = $oConfig->Get('branding', 'login_logo', ''); - $aResult['LoginDescription'] = $oConfig->Get('branding', 'login_desc', ''); + $aResult['LoginBackground'] = $oConfig->Get('branding', 'login_background', ''); $aResult['LoginCss'] = $oConfig->Get('branding', 'login_css', ''); + $aResult['LoginDescription'] = $oConfig->Get('branding', 'login_desc', ''); $aResult['LoginPowered'] = !!$oConfig->Get('branding', 'login_powered', true); $aResult['UserLogo'] = $oConfig->Get('branding', 'user_logo', ''); + $aResult['UserCss'] = $oConfig->Get('branding', 'user_css', ''); } $aResult['LoadingDescriptionEsc'] = \htmlspecialchars($aResult['LoadingDescription'], ENT_QUOTES|ENT_IGNORE, 'UTF-8'); @@ -1134,6 +1140,8 @@ class Actions $oAccount = $this->getAccountFromToken(false); if ($oAccount instanceof \RainLoop\Model\Account) { + $aResult['IncludeCss'] = $aResult['UserCss']; + $oAddressBookProvider = $this->AddressBookProvider($oAccount); $aResult['Auth'] = true; @@ -1186,6 +1194,9 @@ class Actions { $oAccount = null; + $aResult['IncludeBackground'] = $aResult['LoginBackground']; + $aResult['IncludeCss'] = $aResult['LoginCss']; + $aResult['DevEmail'] = $oConfig->Get('labs', 'dev_email', ''); $aResult['DevPassword'] = $oConfig->Get('labs', 'dev_password', ''); } @@ -2707,17 +2718,19 @@ class Actions $this->setConfigFromParams($oConfig, 'DetermineUserLanguage', 'login', 'determine_user_language', 'bool'); $this->setConfigFromParams($oConfig, 'DetermineUserDomain', 'login', 'determine_user_domain', 'bool'); - if ($this->HasOneOfActionParams(array('Title', 'LoadingDescription', 'LoginLogo', 'LoginDescription', 'LoginCss', 'LoginPowered', 'UserLogo')) && $this->PremType()) + if ($this->HasOneOfActionParams(array('Title', 'LoadingDescription', 'LoginLogo', 'LoginBackground', 'LoginDescription', 'LoginCss', 'LoginPowered', 'UserLogo', 'UserCss')) && $this->PremType()) { $this->setConfigFromParams($oConfig, 'Title', 'webmail', 'title', 'string'); $this->setConfigFromParams($oConfig, 'LoadingDescription', 'webmail', 'loading_description', 'string'); $this->setConfigFromParams($oConfig, 'LoginLogo', 'branding', 'login_logo', 'string'); + $this->setConfigFromParams($oConfig, 'LoginBackground', 'branding', 'login_background', 'string'); $this->setConfigFromParams($oConfig, 'LoginDescription', 'branding', 'login_desc', 'string'); $this->setConfigFromParams($oConfig, 'LoginCss', 'branding', 'login_css', 'string'); $this->setConfigFromParams($oConfig, 'LoginPowered', 'branding', 'login_powered', 'bool'); $this->setConfigFromParams($oConfig, 'UserLogo', 'branding', 'user_logo', 'string'); + $this->setConfigFromParams($oConfig, 'UserCss', 'branding', 'user_css', 'string'); } $this->setConfigFromParams($oConfig, 'TokenProtection', 'security', 'csrf_protection', 'bool'); 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 b38369d40..c948259cb 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 @@ -86,10 +86,12 @@ class Application extends \RainLoop\Config\AbstractConfig 'branding' => array( 'login_logo' => array(''), + 'login_background' => array(''), 'login_desc' => array(''), 'login_css' => array(''), 'login_powered' => array(true), - 'user_logo' => array('') + 'user_logo' => array(''), + 'user_css' => array('') ), 'contacts' => array( diff --git a/rainloop/v/0.0.0/app/templates/Index.html b/rainloop/v/0.0.0/app/templates/Index.html index d537e1f34..330cf2820 100644 --- a/rainloop/v/0.0.0/app/templates/Index.html +++ b/rainloop/v/0.0.0/app/templates/Index.html @@ -34,6 +34,11 @@ window.document.getElementById('rlThemeLink').href = window.rainloopAppData['NewThemeLink']; } + @@ -89,13 +94,25 @@ window.$LAB .script(function () { return [ - {'src': '{{BaseAppLibsScriptLink}}', 'type': 'text/javascript', 'charset': 'utf-8'}, + {'src': '{{BaseAppLibsScriptLink}}', 'type': 'text/javascript', 'charset': 'utf-8'} + ]; + }) + .wait(function () { + __simplePace(20); + if (window.rainloopAppData['IncludeBackground']) { + $('#rl-bg').attr('style', 'background-image: none !important;').backstretch(window.rainloopAppData['IncludeBackground'], { + 'fade': 1000 + }).removeAttr('style'); + } + }) + .script(function () { + return [ {'src': window.rainloopAppData['TemplatesLink'], 'type': 'text/javascript', 'charset': 'utf-8'}, {'src': window.rainloopAppData['LangLink'], 'type': 'text/javascript', 'charset': 'utf-8'} ]; }) .wait(function () { - __simplePace(30); + __simplePace(10); }) .script(function () { return {'src': '{{BaseAppMainScriptLink}}', 'type': 'text/javascript', 'charset': 'utf-8'}; diff --git a/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsBranding.html b/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsBranding.html index d9b7db46d..a645652f4 100644 --- a/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsBranding.html +++ b/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsBranding.html @@ -42,7 +42,7 @@

- Login Page + Login
+
+
+
+ +
+
- +
+ +
+
+
--->
\ No newline at end of file diff --git a/rainloop/v/0.0.0/app/templates/Views/User/Login.html b/rainloop/v/0.0.0/app/templates/Views/User/Login.html index e6c597207..2f0b2f705 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/Login.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/Login.html @@ -1,4 +1,3 @@ -