diff --git a/src/_posts/2019-08-30-civicrm-mailing-validation.md b/src/_posts/2019-08-30-civicrm-mailing-validation.md index 9651b02..7b4e40f 100644 --- a/src/_posts/2019-08-30-civicrm-mailing-validation.md +++ b/src/_posts/2019-08-30-civicrm-mailing-validation.md @@ -29,7 +29,9 @@ duplicate mailing names for our client… and now for you, too! Using [`civix`](https://github.com/totten/civix), we set up a new CiviCRM extension for our code: - civix generate:module mailing +```sh +$ civix generate:module mailing +``` (We called our extension `mailing`, because our creative director was occupied with an Art at the time) @@ -45,20 +47,21 @@ an AngularJS page loads, and you can use a `ChangeSet` to edit an AngularJS template. Because AngularJS templates specify form logic, this also lets you change the validation behaviour. Our hook function looks like this: - function mailing_civicrm_alterAngular($angular) { - $changeSet = \Civi\Angular\ChangeSet::create('mailing_name_unique') - ->alterHtml('~/crmMailing/BlockSummary.html', function(phpQueryObject $doc) { - // name validation - $doc->find('.crm-group:has([crm-ui-id="subform.mailingName"])')->attr('ng-controller', 'NameValidateCtrl'); - $doc->find('[crm-ui-id="subform.mailingName"]')->attr('ng-blur', 'validateName(mailing, \'name\')'); - $doc->find('[crm-ui-id="subform.mailingName"]')->attr('crm-ui-validate', 'isValid'); - }); +```php +function mailing_civicrm_alterAngular($angular) { + $changeSet = \Civi\Angular\ChangeSet::create('mailing_name_unique') + ->alterHtml('~/crmMailing/BlockSummary.html', function(phpQueryObject $doc) { + // name validation + $doc->find('.crm-group:has([crm-ui-id="subform.mailingName"])')->attr('ng-controller', 'NameValidateCtrl'); + $doc->find('[crm-ui-id="subform.mailingName"]')->attr('ng-blur', 'validateName(mailing, \'name\')'); + $doc->find('[crm-ui-id="subform.mailingName"]')->attr('crm-ui-validate', 'isValid'); + }); - $angular->add($changeSet); - - CRM_Core_Resources::singleton()->addScriptFile('mailing', 'js/disallow-duplicate-names.js'); - } + $angular->add($changeSet); + CRM_Core_Resources::singleton()->addScriptFile('mailing', 'js/disallow-duplicate-names.js'); +} +``` Setting `crm-ui-validate` to `validateName` directly fired the event _way_ too many times, so instead `validateName` is only called when focus leaves the @@ -70,40 +73,42 @@ field, `ng-blur`, which then sets the `isValid` variable that's checked by Then, the code which queries the CiviCRM API to check for mailings with duplicate names: - var validating = false; - - (function(angular, $) { - var crmMailing = angular.module('crmMailing'); - - crmMailing.controller('NameValidateCtrl', function($scope) { - $scope.isValid = false; - - $scope.validateName = function(mailing, field) { - if (!validating) { - validating = true; - - CRM.api3('Mailing', 'get', { - "sequential": 1, - "name": mailing[field], - "id": {"!=": mailing.id} - }).then(function(result) { - // do something with result - if (result.count > 0 ) { - $scope.isValid = false; - CRM.alert(ts('There is already a mailing with this name; sending this one will crash CiviCRM!')); - } else { - $scope.isValid = true; - } - }, function(error) { - // oops - console.log(error); - }); - - validating = false; - } - }; - }); - })(angular, CRM.$); +```javascript +var validating = false; + +(function(angular, $) { + var crmMailing = angular.module('crmMailing'); + + crmMailing.controller('NameValidateCtrl', function($scope) { + $scope.isValid = false; + + $scope.validateName = function(mailing, field) { + if (!validating) { + validating = true; + + CRM.api3('Mailing', 'get', { + "sequential": 1, + "name": mailing[field], + "id": {"!=": mailing.id} + }).then(function(result) { + // do something with result + if (result.count > 0 ) { + $scope.isValid = false; + CRM.alert(ts('There is already a mailing with this name; sending this one will crash CiviCRM!')); + } else { + $scope.isValid = true; + } + }, function(error) { + // oops + console.log(error); + }); + + validating = false; + } + }; + }); +})(angular, CRM.$); +``` (saved as `js/disallow-duplicate-names.js`) @@ -111,7 +116,9 @@ duplicate names: Activate the extension, e.g. with `cv` - $ cv en mailing +``` +$ cv en mailing +``` Now, open a mailing and try to give it the same name as an existing one – you should see the field border turn red, and you'll be prevented from continuing or diff --git a/src/assets/styles/components/_highlighting.scss b/src/assets/styles/components/_highlighting.scss new file mode 100644 index 0000000..488dd95 --- /dev/null +++ b/src/assets/styles/components/_highlighting.scss @@ -0,0 +1,73 @@ +pre.highlight { + color: #93A1A1; + background-color: #002B36; + + .c { color: #586E75 } /* Comment */ + .err { color: #93A1A1 } /* Error */ + .g { color: #93A1A1 } /* Generic */ + .k { color: #859900 } /* Keyword */ + .l { color: #93A1A1 } /* Literal */ + .n { color: #93A1A1 } /* Name */ + .o { color: #859900 } /* Operator */ + .x { color: #CB4B16 } /* Other */ + .p { color: #93A1A1 } /* Punctuation */ + .cm { color: #586E75 } /* Comment.Multiline */ + .cp { color: #859900 } /* Comment.Preproc */ + .c1 { color: #586E75 } /* Comment.Single */ + .cs { color: #859900 } /* Comment.Special */ + .gd { color: #2AA198 } /* Generic.Deleted */ + .ge { color: #93A1A1; font-style: italic } /* Generic.Emph */ + .gr { color: #DC322F } /* Generic.Error */ + .gh { color: #CB4B16 } /* Generic.Heading */ + .gi { color: #859900 } /* Generic.Inserted */ + .go { color: #93A1A1 } /* Generic.Output */ + .gp { color: #93A1A1 } /* Generic.Prompt */ + .gs { color: #93A1A1; font-weight: bold } /* Generic.Strong */ + .gu { color: #CB4B16 } /* Generic.Subheading */ + .gt { color: #93A1A1 } /* Generic.Traceback */ + .kc { color: #CB4B16 } /* Keyword.Constant */ + .kd { color: #268BD2 } /* Keyword.Declaration */ + .kn { color: #859900 } /* Keyword.Namespace */ + .kp { color: #859900 } /* Keyword.Pseudo */ + .kr { color: #268BD2 } /* Keyword.Reserved */ + .kt { color: #DC322F } /* Keyword.Type */ + .ld { color: #93A1A1 } /* Literal.Date */ + .m { color: #2AA198 } /* Literal.Number */ + .s { color: #2AA198 } /* Literal.String */ + .na { color: #93A1A1 } /* Name.Attribute */ + .nb { color: #B58900 } /* Name.Builtin */ + .nc { color: #268BD2 } /* Name.Class */ + .no { color: #CB4B16 } /* Name.Constant */ + .nd { color: #268BD2 } /* Name.Decorator */ + .ni { color: #CB4B16 } /* Name.Entity */ + .ne { color: #CB4B16 } /* Name.Exception */ + .nf { color: #268BD2 } /* Name.Function */ + .nl { color: #93A1A1 } /* Name.Label */ + .nn { color: #93A1A1 } /* Name.Namespace */ + .nx { color: #93A1A1 } /* Name.Other */ + .py { color: #93A1A1 } /* Name.Property */ + .nt { color: #268BD2 } /* Name.Tag */ + .nv { color: #268BD2 } /* Name.Variable */ + .ow { color: #859900 } /* Operator.Word */ + .w { color: #93A1A1 } /* Text.Whitespace */ + .mf { color: #2AA198 } /* Literal.Number.Float */ + .mh { color: #2AA198 } /* Literal.Number.Hex */ + .mi { color: #2AA198 } /* Literal.Number.Integer */ + .mo { color: #2AA198 } /* Literal.Number.Oct */ + .sb { color: #586E75 } /* Literal.String.Backtick */ + .sc { color: #2AA198 } /* Literal.String.Char */ + .sd { color: #93A1A1 } /* Literal.String.Doc */ + .s2 { color: #2AA198 } /* Literal.String.Double */ + .se { color: #CB4B16 } /* Literal.String.Escape */ + .sh { color: #93A1A1 } /* Literal.String.Heredoc */ + .si { color: #2AA198 } /* Literal.String.Interpol */ + .sx { color: #2AA198 } /* Literal.String.Other */ + .sr { color: #DC322F } /* Literal.String.Regex */ + .s1 { color: #2AA198 } /* Literal.String.Single */ + .ss { color: #2AA198 } /* Literal.String.Symbol */ + .bp { color: #268BD2 } /* Name.Builtin.Pseudo */ + .vc { color: #268BD2 } /* Name.Variable.Class */ + .vg { color: #268BD2 } /* Name.Variable.Global */ + .vi { color: #268BD2 } /* Name.Variable.Instance */ + .il { color: #2AA198 } /* Literal.Number.Integer.Long */ +} diff --git a/src/assets/styles/main.scss b/src/assets/styles/main.scss index c4373ca..338bffa 100644 --- a/src/assets/styles/main.scss +++ b/src/assets/styles/main.scss @@ -43,6 +43,7 @@ @import 'components/header'; @import 'components/banner'; @import 'components/contact'; +@import 'components/highlighting'; /* Main */