diff --git a/.gitignore b/.gitignore index 06fd278b8..b9a13560a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,10 @@ /npm-debug.log /rainloop.sublime-project /rainloop.sublime-workspace +/rainloop/v/0.0.0/static/fonts/* +!/rainloop/v/0.0.0/static/fonts/.gitempty +/rainloop/v/0.0.0/static/svg/* +!/rainloop/v/0.0.0/static/svg/.gitempty /rainloop/v/0.0.0/static/css/*.css /rainloop/v/0.0.0/static/js/**/*.js /node_modules diff --git a/dev/App/Abstract.js b/dev/App/Abstract.js index 98c6a8a74..9c11006b7 100644 --- a/dev/App/Abstract.js +++ b/dev/App/Abstract.js @@ -283,6 +283,11 @@ AbstractApp.prototype.bootstart = function () { + if (window.console && Utils.isFunc(window.console.log)) + { + window.console.log('Psss, hacker! There is nothing interesting :)'); + } + Events.pub('rl.bootstart'); var @@ -293,10 +298,11 @@ ko.components.register('SaveTrigger', require('Component/SaveTrigger')); ko.components.register('Input', require('Component/Input')); ko.components.register('Select', require('Component/Select')); - ko.components.register('TextArea', require('Component/TextArea')); ko.components.register('Radio', require('Component/Radio')); + ko.components.register('TextArea', require('Component/TextArea')); ko.components.register('x-script', require('Component/Script')); + ko.components.register('svg-icon', require('Component/SvgIcon')); if (/**false && /**/Settings.settingsGet('MaterialDesign') && Globals.bAnimationSupported) { diff --git a/dev/Component/SvgIcon.js b/dev/Component/SvgIcon.js new file mode 100644 index 000000000..710754c6f --- /dev/null +++ b/dev/Component/SvgIcon.js @@ -0,0 +1,35 @@ + +(function () { + + 'use strict'; + + var + $ = require('$'), + sUrl = null, + getUtl = function () { + if (!sUrl) + { + sUrl = 'rainloop/v/' + ($('#rlAppVersion').attr('content') || '0.0.0') + '/static/css/svg/icons.svg'; + } + + return sUrl; + } + ; + + module.exports = { + viewModel: { + createViewModel: function(oParams, oComponentInfo) { + var icon = oParams.icon || 'null'; + if (oComponentInfo.element && oComponentInfo.element) + { + $(oComponentInfo.element).replaceWith( +''); + } + } + }, + 'template': '' + }; + +}()); + + diff --git a/dev/Model/Filter.js b/dev/Model/Filter.js index e919427d0..1bf577f90 100644 --- a/dev/Model/Filter.js +++ b/dev/Model/Filter.js @@ -43,6 +43,9 @@ this.actionValueSecond = ko.observable(''); this.actionValueThird = ko.observable(''); + this.actionValueFourth = ko.observable(''); + this.actionValueFourth.error = ko.observable(false); + this.actionMarkAsRead = ko.observable(false); this.actionKeep = ko.observable(true); @@ -55,6 +58,8 @@ this.actionValue.error(false); this.actionValueSecond(''); this.actionValueThird(''); + this.actionValueFourth(''); + this.actionValueFourth.error(false); }, this); var fGetRealFolderName = function (sFolderFullNameRaw) { @@ -189,6 +194,14 @@ return false; } + if (Enums.FiltersAction.Vacation === this.actionType() && + ('' === this.actionValueFourth() || -1 === this.actionValueFourth().indexOf('@')) + ) + { + this.actionValueFourth.error(true); + return false; + } + this.name.error(false); this.actionValue.error(false); @@ -209,6 +222,7 @@ 'ActionValue': this.actionValue(), 'ActionValueSecond': this.actionValueSecond(), 'ActionValueThird': this.actionValueThird(), + 'ActionValueFourth': this.actionValueFourth(), 'ActionType': this.actionType(), 'Stop': this.actionNoStop() ? '0' : '1', @@ -228,6 +242,11 @@ Utils.delegateRunOnDestroy(oConditionToDelete); }; + FilterModel.prototype.setRecipients = function () + { + this.actionValueFourth(require('Stores/User/Account').accountsEmails().join(', ')); + }; + FilterModel.prototype.parse = function (oItem) { var bResult = false; @@ -255,6 +274,7 @@ this.actionValue(Utils.pString(oItem['ActionValue'])); this.actionValueSecond(Utils.pString(oItem['ActionValueSecond'])); this.actionValueThird(Utils.pString(oItem['ActionValueThird'])); + this.actionValueFourth(Utils.pString(oItem['ActionValueFourth'])); this.actionNoStop(!oItem['Stop']); this.actionKeep(!!oItem['Keep']); @@ -288,6 +308,7 @@ oClone.actionValueSecond(this.actionValueSecond()); oClone.actionValueThird(this.actionValueThird()); + oClone.actionValueFourth(this.actionValueFourth()); oClone.actionKeep(this.actionKeep()); oClone.actionNoStop(this.actionNoStop()); diff --git a/dev/Styles/Filter.less b/dev/Styles/Filter.less index d4860ab6e..3bbdd8af6 100644 --- a/dev/Styles/Filter.less +++ b/dev/Styles/Filter.less @@ -10,5 +10,10 @@ .button-delete { cursor: pointer; } + + .setRecipientsBtn { + margin-top: -2px; + margin-left: 5px; + } } } diff --git a/dev/Styles/SettingsOpenPGP.less b/dev/Styles/SettingsOpenPGP.less index 3397d2922..894ca86b9 100644 --- a/dev/Styles/SettingsOpenPGP.less +++ b/dev/Styles/SettingsOpenPGP.less @@ -17,8 +17,13 @@ } .open-pgp-key-img { - font-size: 12px; - margin-right: 5px; + + margin-right: 10px; + + .svg-icon { + width: 12px; + height: 12px; + } } .open-pgp-key-id, .open-pgp-key-user { @@ -49,7 +54,11 @@ .delete-open-pgp-key, .view-open-pgp-key { cursor: pointer; - opacity: 0.5; + opacity: 0.7; + + &:hover { + opacity: 0.9; + } } } } \ No newline at end of file diff --git a/dev/Styles/_BootstrapFix.less b/dev/Styles/_BootstrapFix.less index add5ae9c9..3c7544cd0 100644 --- a/dev/Styles/_BootstrapFix.less +++ b/dev/Styles/_BootstrapFix.less @@ -67,6 +67,10 @@ select { width: 223px; } +.btn .svg-icon { + vertical-align: middle; +} + .btn-small.btn-small-small { padding: 3px 9px; font-size: 11px; diff --git a/dev/Styles/_End.less b/dev/Styles/_End.less index 5fcebedea..d48b671d8 100644 --- a/dev/Styles/_End.less +++ b/dev/Styles/_End.less @@ -69,3 +69,22 @@ } } } + +svg-icon { + display: none; +} + +.svg-icon { + border: 0; + outline: 0; + + height: 16px; + width: 16px; + display: inline-block; + fill: #333; + + &.svg-icon-archive { + height: 14px; + width: 14px; + } +} diff --git a/dev/View/Popup/ComposeOpenPgp.js b/dev/View/Popup/ComposeOpenPgp.js index 2ead22bc8..70625f669 100644 --- a/dev/View/Popup/ComposeOpenPgp.js +++ b/dev/View/Popup/ComposeOpenPgp.js @@ -30,7 +30,7 @@ var self = this; - this.optionsCaption = Translator.i18n('@i18n/Add a public key'); + this.optionsCaption = Translator.i18n('PGP_NOTIFICATIONS/ADD_A_PUBLICK_KEY'); this.notification = ko.observable(''); @@ -208,6 +208,10 @@ }, 10); } + else + { + self.submitRequest(false); + } return bResult; diff --git a/gulpfile.js b/gulpfile.js index 540b0a738..d38f654cd 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -494,12 +494,21 @@ gulp.task('fontastic-fonts:clear', function() { return cleanDir('rainloop/v/' + cfg.devVersion + '/static/css/fonts/rainloop.*'); }); +gulp.task('fontastic-svg:clear', function() { + return cleanDir('rainloop/v/' + cfg.devVersion + '/static/css/svg/*.svg'); +}); + gulp.task('fontastic-fonts:copy', ['fontastic-fonts:clear'], function() { return gulp.src('vendors/fontastic/fonts/rainloop.*') .pipe(gulp.dest('rainloop/v/' + cfg.devVersion + '/static/css/fonts')); }); -gulp.task('fontastic', ['fontastic-fonts:copy']); +gulp.task('fontastic-svg:copy', ['fontastic-svg:clear'], function() { + return gulp.src('vendors/fontastic/svg/*.svg') + .pipe(gulp.dest('rainloop/v/' + cfg.devVersion + '/static/css/svg')); +}); + +gulp.task('fontastic', ['fontastic-fonts:copy', 'fontastic-svg:copy']); gulp.task('ckeditor:clear', function() { return cleanDir('rainloop/v/' + cfg.devVersion + '/static/ckeditor'); diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Filters/Classes/Filter.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Filters/Classes/Filter.php index 360a2c074..09c181344 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Filters/Classes/Filter.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Filters/Classes/Filter.php @@ -49,6 +49,11 @@ class Filter */ private $sActionValueThird; + /** + * @var string + */ + private $sActionValueFourth; + /** * @var bool */ @@ -89,6 +94,7 @@ class Filter $this->sActionValue = ''; $this->sActionValueSecond = ''; $this->sActionValueThird = ''; + $this->sActionValueFourth = ''; $this->bMarkAsRead = false; $this->bKeep = true; @@ -167,6 +173,14 @@ class Filter return $this->sActionValueThird; } + /** + * @return string + */ + public function ActionValueFourth() + { + return $this->sActionValueFourth; + } + /** * @return bool */ @@ -236,6 +250,7 @@ class Filter $this->sActionValue = isset($aFilter['ActionValue']) ? $aFilter['ActionValue'] : ''; $this->sActionValueSecond = isset($aFilter['ActionValueSecond']) ? $aFilter['ActionValueSecond'] : ''; $this->sActionValueThird = isset($aFilter['ActionValueThird']) ? $aFilter['ActionValueThird'] : ''; + $this->sActionValueFourth = isset($aFilter['ActionValueFourth']) ? $aFilter['ActionValueFourth'] : ''; $this->bKeep = isset($aFilter['Keep']) ? '1' === (string) $aFilter['Keep'] : true; $this->bStop = isset($aFilter['Stop']) ? '1' === (string) $aFilter['Stop'] : true; @@ -276,6 +291,7 @@ class Filter 'ActionValue' => $this->ActionValue(), 'ActionValueSecond' => $this->ActionValueSecond(), 'ActionValueThird' => $this->ActionValueThird(), + 'ActionValueFourth' => $this->ActionValueFourth(), 'Keep' => $this->Keep(), 'Stop' => $this->Stop(), 'MarkAsRead' => $this->MarkAsRead() diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Filters/SieveStorage.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Filters/SieveStorage.php index a83e8d8ef..590dc9568 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Filters/SieveStorage.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Filters/SieveStorage.php @@ -350,6 +350,7 @@ class SieveStorage implements \RainLoop\Providers\Filters\FiltersInterface $sValue = \trim($oFilter->ActionValue()); $sValueSecond = \trim($oFilter->ActionValueSecond()); $sValueThird = \trim($oFilter->ActionValueThird()); + $sValueFourth = \trim($oFilter->ActionValueFourth()); if (0 < \strlen($sValue)) { $aCapa['vacation'] = true; @@ -367,8 +368,24 @@ class SieveStorage implements \RainLoop\Providers\Filters\FiltersInterface $iDays = (int) $sValueThird; } - $aResult[] = $sTab.'vacation :days '.$iDays.' '.$sSubject.'"'.$this->quote($sValue).'";'; + $sAddresses = ''; + if (0 < \strlen($sValueFourth)) + { + $self = $this; + $aAddresses = \explode(',', $sValueFourth); + $aAddresses = \array_filter(\array_map(function ($sEmail) use ($self) { + $sEmail = \trim($sEmail); + return 0 < \strlen($sEmail) ? '"'.$self->quote($sEmail).'"' : ''; + }, $aAddresses), 'strlen'); + + if (0 < \count($aAddresses)) + { + $sAddresses = ':addresses ['.\implode(', ', $aAddresses).'] '; + } + } + + $aResult[] = $sTab.'vacation :days '.$iDays.' '.$sAddresses.$sSubject.'"'.$this->quote($sValue).'";'; if ($oFilter->Stop()) { $aResult[] = $sTab.'stop;'; @@ -523,7 +540,7 @@ class SieveStorage implements \RainLoop\Providers\Filters\FiltersInterface * * @return string */ - private function quote($sValue) + public function quote($sValue) { return \str_replace(array('\\', '"'), array('\\\\', '\\"'), \trim($sValue)); } 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 040f04945..903e615c1 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Service.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Service.php @@ -266,6 +266,7 @@ class Service '{{BaseAppOpenPgpScriptLink}}' => $aData['OpenPgpJsLink'], '{{BaseAppMainCommonScriptLink}}' => $aData['AppJsCommonLink'], '{{BaseAppMainScriptLink}}' => $aData['AppJsLink'], + '{{BaseVersion}}' => APP_VERSION, '{{BaseDir}}' => 'ltr' // '{{BaseDir}}' => \in_array($aData['Language'], array('ar', 'he', 'ur')) ? 'rtl' : 'ltr' ); diff --git a/rainloop/v/0.0.0/app/templates/Index.html b/rainloop/v/0.0.0/app/templates/Index.html index da86d980c..09aae0104 100644 --- a/rainloop/v/0.0.0/app/templates/Index.html +++ b/rainloop/v/0.0.0/app/templates/Index.html @@ -19,6 +19,7 @@ +
This SVG sprite was created with Fontastic
+ +http://yoursite.com/images/icons.svg
<svg class="icon-twitter"><use xlink:href="#icon-twitter"></use></svg>
+
/images/icons.svg
. So now the twitter icon markup could look like this:<svg class="icon-twitter"><use xlink:href="/images/icons.svg#icon-twitter"></use></svg>
+
[class^="icon-"], [class*=" icon-"] {
+ height: 32px;
+ width: 32px;
+ display: inline-block;
+ fill: currentColor;
+}
+
You can use height
and width
properties to control icon size. To define icon color use fill
property. Note: currentColor
keyword inherits the color value of a parent element.
Remember: with Icon Cloud you don't have to worry about IE capability, the sprite file and other stuff. We'll take care of all of that for you, just click the "Publish" button and give it a try.