gscan 4.29.0 → 4.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/app/tpl/error.hbs CHANGED
@@ -7,6 +7,6 @@
7
7
  <p>{{context}}</p>
8
8
  <p>{{details}}</p>
9
9
  <p>{{stack}}</p>
10
- <p>Please visit our <a href="https://forum.ghost.org">forum</a> and let us know!</p>
10
+ <p>Please visit our <a href="https://forum.ghost.org/">forum</a> and let us know!</p>
11
11
  </section>
12
12
  </div>
package/app/tpl/index.hbs CHANGED
@@ -22,8 +22,8 @@
22
22
  <span class="gh-input-icon select-arrow">{{> icon-arrow-up}}</span>
23
23
  <span class="gh-input-icon select-arrow active">{{> icon-arrow-down}}</span>
24
24
  <select class="gh-input gh-select" name="version" id="version">
25
- <option value="v5">{{ghostVersions.v5.major}}</option>
26
- <option value="v4" selected>{{ghostVersions.v4.major}}</option>
25
+ <option value="v5" selected>{{ghostVersions.v5.major}}</option>
26
+ <option value="v4">{{ghostVersions.v4.major}}</option>
27
27
  <option value="v3">{{ghostVersions.v3.major}}</option>
28
28
  <option value="v2">{{ghostVersions.v2.major}}</option>
29
29
  <option value="v1">{{ghostVersions.v1.major}}</option>
@@ -57,8 +57,8 @@
57
57
 
58
58
  </div>
59
59
  <div class="gh-navbar-right">
60
- <a class="gh-navbar-item" href="https://ghost.org/docs/api/handlebars-themes/" target="blank" rel="noopener">Theme Docs</a>
61
- <a class="gh-navbar-btn gh-btn" href="https://ghost.org"><span>Ghost.org</span></a>
60
+ <a class="gh-navbar-item" href="https://ghost.org/docs/themes/" target="blank" rel="noopener">Theme Docs</a>
61
+ <a class="gh-navbar-btn gh-btn" href="https://ghost.org/"><span>Ghost.org</span></a>
62
62
  </div>
63
63
  </nav>
64
64
  </header>
@@ -99,8 +99,8 @@
99
99
  </nav>
100
100
  <div class="gh-mobilemenu">
101
101
  <a class="gh-navbar-item" href="https://gscan.ghost.org">GScan</a>
102
- <a class="gh-navbar-item" href="https://ghost.org/docs/api/handlebars-themes/">Theme Docs</a>
103
- <a class="gh-navbar-item" href="https://ghost.org">Ghost.org</a>
102
+ <a class="gh-navbar-item" href="https://ghost.org/docs/themes/">Theme Docs</a>
103
+ <a class="gh-navbar-item" href="https://ghost.org/">Ghost.org</a>
104
104
  </div>
105
105
  </header>
106
106
 
@@ -112,9 +112,9 @@
112
112
 
113
113
  <footer class="gh-foot">
114
114
  <div class="gh-foot-support inner">
115
- <div class="gh-support-email" href="https://ghost.org/pricing">
115
+ <div class="gh-support-email" href="https://ghost.org/pricing/">
116
116
  <h4>Ready to upgrade to the best?</h4>
117
- <p>Spend less time running your servers and more time running your site. <strong><a href="https://ghost.org/pricing">Ghost(Pro)</a></strong>
117
+ <p>Spend less time running your servers and more time running your site. <strong><a href="https://ghost.org/pricing/">Ghost(Pro)</a></strong>
118
118
  has got you covered</p>
119
119
  </div>
120
120
  <div class="gh-support-slack">
@@ -125,7 +125,7 @@
125
125
  </div>
126
126
  <div class="gh-foot-content inner">
127
127
  <nav class="gh-foot-min-nav">
128
- <a class="gh-foot-min-item gh-foot-min-logo" href="https://ghost.org">
128
+ <a class="gh-foot-min-item gh-foot-min-logo" href="https://ghost.org/">
129
129
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 294" preserveAspectRatio="xMidYMid meet" class="ghost-orb-logo-wh">
130
130
  <title>Ghost Logo</title>
131
131
  <style>
@@ -154,7 +154,7 @@
154
154
  <a class="gh-foot-min-item" href="https://twitter.com/ghost">@Ghost</a>
155
155
  </nav>
156
156
  <div class="gh-foot-min-back">
157
- <a class="gh-foot-min-item" href="https://ghost.org">Back to Ghost</a>
157
+ <a class="gh-foot-min-item" href="https://ghost.org/">Back to Ghost</a>
158
158
  </div>
159
159
  </div>
160
160
  </footer>
package/bin/cli.js CHANGED
@@ -136,7 +136,7 @@ function outputResult(result, options) {
136
136
  let message = failure.ref;
137
137
 
138
138
  if (failure.message) {
139
- message += `- ${failure.message}`;
139
+ message += ` - ${failure.message}`;
140
140
  }
141
141
  ui.log(message);
142
142
  });
@@ -221,7 +221,7 @@ function outputResults(theme, options) {
221
221
  _.each(theme.results.recommendation, rule => outputResult(rule, options));
222
222
  }
223
223
 
224
- ui.log(`\nGet more help at ${chalk.cyan.underline('https://ghost.org/docs/api/handlebars-themes/')}`);
224
+ ui.log(`\nGet more help at ${chalk.cyan.underline('https://ghost.org/docs/themes/')}`);
225
225
  ui.log(`You can also check theme compatibility at ${chalk.cyan.underline('https://gscan.ghost.org/')}`);
226
226
 
227
227
  // The CLI feature is mainly used to run gscan programatically in tests within themes.
@@ -7,7 +7,11 @@ module.exports = {
7
7
  'GS090-NO-PRODUCT-DATA-HELPER': require('./lint-no-product-data-helper'),
8
8
  'GS090-NO-PRODUCTS-DATA-HELPER': require('./lint-no-products-data-helper'),
9
9
  'GS090-NO-MEMBER-PRODUCTS-DATA-HELPER': require('./lint-no-member-products-data-helper'),
10
- 'GS090-NO-PRICE-DATA-HELPER': require('./lint-no-price-data-helper'),
10
+ 'GS090-NO-TIER-PRICE-AS-OBJECT': require('./lint-no-tier-price-as-object'),
11
+ 'GS090-NO-TIER-BENEFIT-AS-OBJECT': require('./lint-no-tier-benefit-as-object'),
12
+ 'GS090-NO-PRICE-DATA-CURRENCY-GLOBAL': require('./lint-no-price-data-currency-global'),
13
+ 'GS090-NO-PRICE-DATA-CURRENCY-CONTEXT': require('./lint-no-price-data-currency-context'),
14
+ 'GS090-NO-PRICE-DATA-MONTHLY-YEARLY': require('./lint-no-price-data-monthly-yearly'),
11
15
  'no-multi-param-conditionals': require('./lint-no-multi-param-conditionals'),
12
16
  'no-nested-async-helpers': require('./lint-no-nested-async-helpers'),
13
17
  'no-prev-next-post-outside-post-context': require('./lint-no-prev-next-post-outside-post-context'),
@@ -204,6 +204,20 @@ class Scope {
204
204
  return found;
205
205
  }
206
206
 
207
+ getParentContextNode(context) {
208
+ let matchedFrame = null;
209
+
210
+ if (this.frames && this.frames.length) {
211
+ matchedFrame = this.frames.find((frame) => {
212
+ if (frame.nodeName === context) {
213
+ return true;
214
+ }
215
+ });
216
+ }
217
+
218
+ return matchedFrame && matchedFrame.node;
219
+ }
220
+
207
221
  isLocal(node) {
208
222
  // @foo MustacheStatements are referencing globals rather than locals
209
223
  if (node.type === 'MustacheStatement' && node.path.data) {
@@ -0,0 +1,26 @@
1
+ const Rule = require('./base');
2
+ const _ = require('lodash');
3
+
4
+ module.exports = class NoPriceDataCurrencyContext extends Rule {
5
+ _findPriceData(node) {
6
+ if (node.data && node.parts && node.parts.length === 2 && node.parts[0] === 'price' && node.parts[1] === 'currency') {
7
+ const foreachNode = this.scope.getParentContextNode('foreach');
8
+ const foreachObject = _.get(foreachNode, 'params[0].original');
9
+
10
+ if (['@member.subscriptions', 'tiers'].includes(foreachObject)) {
11
+ this.log(({
12
+ message: `{{@price.currency}} should be replaced with {{currency}}`,
13
+ line: node.loc && node.loc.start.line,
14
+ column: node.loc && node.loc.start.column,
15
+ source: this.sourceForNode(node)
16
+ }));
17
+ }
18
+ }
19
+ }
20
+
21
+ visitor() {
22
+ return {
23
+ PathExpression: this._findPriceData.bind(this)
24
+ };
25
+ }
26
+ };
@@ -0,0 +1,26 @@
1
+ const Rule = require('./base');
2
+ const _ = require('lodash');
3
+
4
+ module.exports = class NoPriceDataCurrencyGlobal extends Rule {
5
+ _findPriceData(node) {
6
+ if (node.data && node.parts && node.parts.length === 2 && node.parts[0] === 'price' && node.parts[1] === 'currency') {
7
+ const foreachNode = this.scope.getParentContextNode('foreach');
8
+ const foreachObject = _.get(foreachNode, 'params[0].original');
9
+
10
+ if (!['@member.subscriptions', 'tiers'].includes(foreachObject)) {
11
+ this.log(({
12
+ message: `{{@price.currency}} should be replaced with {{currency}} and a context`,
13
+ line: node.loc && node.loc.start.line,
14
+ column: node.loc && node.loc.start.column,
15
+ source: this.sourceForNode(node)
16
+ }));
17
+ }
18
+ }
19
+ }
20
+
21
+ visitor() {
22
+ return {
23
+ PathExpression: this._findPriceData.bind(this)
24
+ };
25
+ }
26
+ };
@@ -0,0 +1,20 @@
1
+ const Rule = require('./base');
2
+
3
+ module.exports = class NoPriceDataMonthlyYearly extends Rule {
4
+ _findPriceData(node) {
5
+ if (node.data && node.parts && node.parts.length === 2 && node.parts[0] === 'price' && ['monthly', 'yearly'].includes(node.parts[1])) {
6
+ this.log(({
7
+ message: `{{@price.monthly}} and {{@price.yearly}} should be replaced with {{#get "tiers"}}`,
8
+ line: node.loc && node.loc.start.line,
9
+ column: node.loc && node.loc.start.column,
10
+ source: this.sourceForNode(node)
11
+ }));
12
+ }
13
+ }
14
+
15
+ visitor() {
16
+ return {
17
+ PathExpression: this._findPriceData.bind(this)
18
+ };
19
+ }
20
+ };
@@ -0,0 +1,30 @@
1
+ const Rule = require('./base');
2
+ const {getNodeName, logNode} = require('../helpers');
3
+ const _ = require('lodash');
4
+
5
+ module.exports = class NoTierBenefitAsObject extends Rule {
6
+ _checkForHelperName(node) {
7
+ const nodeName = getNodeName(node);
8
+ const isMatchingHelper = (nodeName.includes('name'));
9
+ const foreachNode = this.scope.getParentContextNode('foreach');
10
+ const getNode = this.scope.getParentContextNode('get');
11
+
12
+ const hasForeachBenefits = _.get(foreachNode, 'params[0].original') === 'benefits';
13
+ const hasGetTiers = _.get(getNode, 'params[0].original') === 'tiers';
14
+
15
+ if (hasForeachBenefits && hasGetTiers && isMatchingHelper) {
16
+ this.log({
17
+ message: `${logNode(node)} should not be used`,
18
+ line: node.loc && node.loc.start.line,
19
+ column: node.loc && node.loc.start.column,
20
+ source: this.sourceForNode(node)
21
+ });
22
+ }
23
+ }
24
+
25
+ visitor() {
26
+ return {
27
+ MustacheStatement: this._checkForHelperName.bind(this)
28
+ };
29
+ }
30
+ };
@@ -1,12 +1,15 @@
1
+ const _ = require('lodash');
1
2
  const Rule = require('./base');
2
3
  const {getNodeName, logNode} = require('../helpers');
3
4
 
4
- module.exports = class NoPriceDataHelper extends Rule {
5
+ module.exports = class NoMonthlyYearlyPriceObjects extends Rule {
5
6
  _checkForHelperName(node) {
6
7
  const nodeName = getNodeName(node);
7
- const isMatchingHelper = (nodeName.startsWith('@price.'));
8
+ const isMatchingHelper = (nodeName.includes('monthly_price.')) || (nodeName.includes('yearly_price.'));
9
+ const getNode = this.scope.getParentContextNode('get');
10
+ const hasGetTiers = _.get(getNode, 'params[0].original') === 'tiers';
8
11
 
9
- if (isMatchingHelper) {
12
+ if (isMatchingHelper && hasGetTiers) {
10
13
  this.log({
11
14
  message: `${logNode(node)} should not be used`,
12
15
  line: node.loc && node.loc.start.line,
@@ -55,7 +55,10 @@ const v4PackageJSONValidationRules = _.extend({},
55
55
  );
56
56
 
57
57
  const canaryPackageJSONConditionalRules = {};
58
- const canaryPackageJSONValidationRules = _.extend({});
58
+ const canaryPackageJSONValidationRules = _.extend({},
59
+ {isNotPresentCardAssets: 'GS010-PJ-GHOST-CARD-ASSETS-NOT-PRESENT'},
60
+ canaryPackageJSONConditionalRules
61
+ );
59
62
 
60
63
  _private.validatePackageJSONFields = function validatePackageJSONFields(packageJSON, theme, packageJSONValidationRules) {
61
64
  let failed = [];
@@ -138,6 +141,11 @@ _private.validatePackageJSONFields = function validatePackageJSONFields(packageJ
138
141
  }
139
142
  }
140
143
 
144
+ // @TODO: validate that the card_assets config is valid
145
+ if (!packageJSON.config || (_.isEmpty(packageJSON.config.card_assets) && !_.isBoolean(packageJSON.config.card_assets))) {
146
+ markFailed('isNotPresentCardAssets');
147
+ }
148
+
141
149
  if (packageJSON.config && packageJSON.config.custom) {
142
150
  const customSettingsKeys = Object.keys(packageJSON.config.custom);
143
151
 
package/lib/format.js CHANGED
@@ -1,30 +1,7 @@
1
1
  const _ = require('lodash');
2
2
  const spec = require('./specs');
3
- const levelWeights = {
4
- error: 10,
5
- warning: 3,
6
- recommendation: 1
7
- };
8
3
  const versions = require('./utils').versions;
9
-
10
- const calcScore = function calcScore(results, stats) {
11
- var maxScore, actualScore, balancedScore;
12
-
13
- maxScore = _.reduce(levelWeights, function (max, weight, level) {
14
- return max + (weight * stats[level]);
15
- }, 0);
16
-
17
- actualScore = _.reduce(levelWeights, function (max, weight, level) {
18
- return max - (weight * _.size(results[level]));
19
- }, maxScore);
20
-
21
- balancedScore = _.ceil((100 / maxScore) * actualScore);
22
-
23
- return {
24
- value: balancedScore,
25
- level: _.size(results.error) > 0 ? 'error' : balancedScore < 60 ? 'warning' : 'passing'
26
- };
27
- };
4
+ const calcScore = require('./utils/score-calculator');
28
5
 
29
6
  const formatCLI = (theme) => {
30
7
  _.each(theme.results, (results) => {
@@ -21,9 +21,15 @@ let rules = {
21
21
  // New rules
22
22
  'GS010-PJ-GHOST-API-PRESENT': {
23
23
  level: 'warning',
24
- rule: 'Remove <code>"engines.ghost-api"</code> from <code>package.json</code>.',
24
+ rule: 'Remove <code>"engines.ghost-api"</code> from <code>package.json</code>',
25
25
  details: oneLineTrim`The <code>"ghost-api"</code> version is no longer used and can be removed.<br>
26
- Find more information about the <code>package.json</code> file <a href="${docsBaseUrl}packagejson/" target=_blank>here</a>.`
26
+ Find more information about the <code>package.json</code> file <a href="${docsBaseUrl}structure/#packagejson" target=_blank>here</a>.`
27
+ },
28
+ 'GS010-PJ-GHOST-CARD-ASSETS-NOT-PRESENT': {
29
+ level: 'warning',
30
+ rule: '<code>"card_assets"</code> will default to <code>true</code> in <code>package.json</code>',
31
+ details: oneLineTrim`The <code>"card_assets"</code> property is enabled by default if not explicitly set.<br>
32
+ Find more information about the <code>card_assets</code> property <a href="${docsBaseUrl}content/#editor-cards" target=_blank>here</a>.`
27
33
  },
28
34
  'GS090-NO-AUTHOR-HELPER-IN-POST-CONTEXT': {
29
35
  level: 'error',
@@ -69,14 +75,46 @@ let rules = {
69
75
  Find more information about the <code>{{@member.subscriptions}}</code> property <a href="${docsBaseUrl}members/#member-subscriptions" target=_blank>here</a>.`,
70
76
  helper: '{{@member.products}}'
71
77
  },
72
- 'GS090-NO-PRICE-DATA-HELPER': {
78
+ 'GS090-NO-PRICE-DATA-CURRENCY-GLOBAL': {
79
+ level: 'error',
80
+ fatal: true,
81
+ rule: 'Replace <code>@price.currency</code> with <code>{{#get "tiers"}}</code> and <code>{{currency}}</code> or <code>{{#foreach @member.subscriptions}}</code> and <code>{{plan.currency}}</code>',
82
+ details: oneLineTrim`There is no longer a global <code>@price</code> object. You need to use either <code>{{#get "tiers"}}</code> to fetch all tiers and use the <code>{{currency}}</code> property of a tier<br>
83
+ or use <code>{{#foreach @member.subscriptions}}</code> to fetch an individual member's subscriptions, and use the <code>{{plan.currency}}</code> property from the subscription.<br>
84
+ Find more information about the <code>{{price}}</code> helper <a href="${docsBaseUrl}members/price/" target=_blank>here</a>.`
85
+ },
86
+ 'GS090-NO-PRICE-DATA-CURRENCY-CONTEXT': {
87
+ level: 'error',
88
+ fatal: true,
89
+ rule: 'Replace <code>{{@price.currency}}</code> with <code>{{currency}}</code> or <code>{{plan.currency}}</code>',
90
+ details: oneLineTrim`There is no longer a global <code>@price</code> object. Instead the <code>{{currency}}</code> property can be used inside <code>{{#get "tiers"}}</code><br>
91
+ or <code>{{plan.currency}}</code> can be used inside <code>{{#foreach @member.subscriptions}}</code><br>
92
+ Find more information about the <code>{{price}}</code> helper <a href="${docsBaseUrl}members/price/" target=_blank>here</a>.`
93
+ },
94
+ 'GS090-NO-PRICE-DATA-MONTHLY-YEARLY': {
95
+ level: 'error',
96
+ fatal: true,
97
+ rule: 'Replace <code>{{@price.monthly}}</code> and <code>{{@price.yearly}}</code> with <code>{{#get "tiers"}}</code> and <code>{{monthly_price}}</code> or <code>{{yearly_price}}</code>',
98
+ details: oneLineTrim`There is no longer a global <code>@price</code> object. You need to use <code>{{#get "tiers"}}</code> to fetch all the tiers and get access to the <code>{{monthly_price}}</code> or <code>{{yearly_price}}</code> for each tier<br>
99
+ Find more information about the <code>{{price}}</code> helper <a href="${docsBaseUrl}helpers/price/" target=_blank>here</a>.`
100
+ },
101
+ 'GS090-NO-TIER-PRICE-AS-OBJECT': {
102
+ level: 'error',
103
+ fatal: true,
104
+ rule: 'Remove usage of <code>{{monthly_price.*}}</code> and <code>{{yearly_price.*}}.</code>',
105
+ details: oneLineTrim`The usage of <code>{{monthly_price.*}}</code> and <code>{{yearly_price.*}} is no longer supported.</code><br>
106
+ ${tierDesc}<br>
107
+ Find more information about the <code>{{#get "tiers"}}</code> <a href="${docsBaseUrl}helpers/tiers/" target=_blank>here</a>.`,
108
+ helper: '{{#get "tiers"}}'
109
+ },
110
+ 'GS090-NO-TIER-BENEFIT-AS-OBJECT': {
73
111
  level: 'error',
74
112
  fatal: true,
75
- rule: 'Replace <code>{{@price}}</code> with <code>{{price}}</code> and <code>{{@member.subscriptions}}</code>',
76
- details: oneLineTrim`The <code>{{@price}}</code> data helper was removed in favor of <code>{{price}}</code> and <code>{{@member.subscriptions}}</code><br>
113
+ rule: 'Remove usage of <code>{{name}}</code> for tier benefits.</code>',
114
+ details: oneLineTrim`The usage of <code>{{name}}</code> for tier benefits is no longer supported.</code><br>
77
115
  ${tierDesc}<br>
78
- Find more information about the <code>{{price}}</code> helper <a href="${docsBaseUrl}members/#the-price-helper" target=_blank>here</a>.`,
79
- helper: '{{@price}}'
116
+ Find more information about the <code>{{#get "tiers"}}</code> <a href="${docsBaseUrl}helpers/tiers/" target=_blank>here</a>.`,
117
+ helper: '{{#get "tiers"}}'
80
118
  },
81
119
 
82
120
  // Updated v1 & v2 rules
@@ -414,7 +452,7 @@ let rules = {
414
452
  rule: 'Replace <code>{{post.author_id}}</code> code with <code>{{post.primary_author.id}}</code>',
415
453
  details: oneLineTrim`The <code>{{post.author_id}}</code> attribute in post context was removed<br>
416
454
  Instead of <code>{{post.author_id}}</code> you need to use <code>{{post.primary_author.id}}</code>.<br>
417
- Find more information about the object attributes of <code>post</code> <a href="${docsBaseUrl}context/post/#post-object-attributes" target=_blank>here</a>.`,
455
+ Find more information about the object attributes of <code>post</code> <a href="${docsBaseUrl}contexts/post/#post-object-attributes" target=_blank>here</a>.`,
418
456
  regex: /{{\s*?post\.author_id\s*?}}/g,
419
457
  helper: '{{post.author_id}}'
420
458
  },
@@ -447,7 +485,7 @@ let rules = {
447
485
  details: oneLineTrim`The <code>cover</code> attribute was replaced with <code>cover_image</code>.<br>
448
486
  Instead of <code>{{author.cover}}</code> you need to use
449
487
  <code>{{primary_author.cover_image}}</code> or <code>{{authors.[#].cover_image}}</code>.<br>
450
- Find more information about the object attributes of <code>author</code> <a href="${docsBaseUrl}context/author/#author-object-attributes" target=_blank>here</a>.`,
488
+ Find more information about the object attributes of <code>author</code> <a href="${docsBaseUrl}contexts/author/#author-object-attributes" target=_blank>here</a>.`,
451
489
  regex: /{{\s*?author\.cover\s*?}}/g,
452
490
  helper: '{{author.cover}}'
453
491
  },
@@ -458,7 +496,7 @@ let rules = {
458
496
  details: oneLineTrim`The <code>image</code> attribute was replaced with <code>profile_image</code>.<br>
459
497
  Instead of <code>{{author.image}}</code>, you need to use
460
498
  <code>{{primary_author.profile_image}}</code> or <code>{{authors.[#].profile_image}}</code>.<br>
461
- Find more information about the object attributes of <code>author</code> <a href="${docsBaseUrl}context/author/#author-object-attributes" target=_blank>here</a>.`,
499
+ Find more information about the object attributes of <code>author</code> <a href="${docsBaseUrl}contexts/author/#author-object-attributes" target=_blank>here</a>.`,
462
500
  regex: /{{\s*?author\.image\s*?}}/g,
463
501
  helper: '{{author.image}}'
464
502
  },
@@ -469,7 +507,7 @@ let rules = {
469
507
  details: oneLineTrim`The <code>cover</code> attribute was replaced with <code>cover_image</code>.<br>
470
508
  Instead of <code>{{post.author.cover}}</code>, you need to use
471
509
  <code>{{post.primary_author.cover_image}}</code> or <code>{{post.authors.[#].cover_image}}</code>.<br>
472
- Find more information about the object attributes of <code>author</code> <a href="${docsBaseUrl}context/author/#author-object-attributes" target=_blank>here</a>.`,
510
+ Find more information about the object attributes of <code>author</code> <a href="${docsBaseUrl}contexts/author/#author-object-attributes" target=_blank>here</a>.`,
473
511
  regex: /{{\s*?post\.author\.cover\s*?}}/g,
474
512
  helper: '{{post.author.cover}}'
475
513
  },
@@ -526,7 +564,7 @@ let rules = {
526
564
  details: oneLineTrim`The <code>image</code> attribute was replaced with <code>profile_image</code>.<br>
527
565
  Instead of <code>{{post.author.image}}</code>, you need to use
528
566
  <code>{{post.primary_author.profile_image}}</code> or <code>{{post.authors.[#].profile_image}}</code>.<br>
529
- Find more information about the object attributes of <code>author</code> <a href="${docsBaseUrl}context/author/#author-object-attributes" target=_blank>here</a>.`,
567
+ Find more information about the object attributes of <code>author</code> <a href="${docsBaseUrl}contexts/author/#author-object-attributes" target=_blank>here</a>.`,
530
568
  regex: /{{\s*?post\.author\.image\s*?}}/g,
531
569
  helper: '{{post.author.image}}'
532
570
  },
@@ -561,7 +599,7 @@ let rules = {
561
599
  details: oneLineTrim`The <code>cover</code> attribute was replaced with <code>cover_image</code>.<br>
562
600
  Instead of <code>{{#if author.cover}}</code>, you need to use
563
601
  <code>{{#if primary_author.cover_image}}</code> or <code>{{#if authors.[#].cover_image}}</code>.<br>
564
- Find more information about the object attributes of <code>author</code> <a href="${docsBaseUrl}context/author/#author-object-attributes" target=_blank>here</a>.`,
602
+ Find more information about the object attributes of <code>author</code> <a href="${docsBaseUrl}contexts/author/#author-object-attributes" target=_blank>here</a>.`,
565
603
  regex: /{{\s*?#if\s*?author\.cover\s*?}}/g,
566
604
  helper: '{{#if author.cover}}'
567
605
  },
@@ -572,7 +610,7 @@ let rules = {
572
610
  details: oneLineTrim`The <code>image</code> attribute was replaced with <code>profile_image</code>.<br>
573
611
  Instead of <code>{{#if author.image}}</code>, you need to use
574
612
  <code>{{#if primary_author.profile_image}}</code> or <code>{{#if authors.[#].profile_image}}</code>.<br>
575
- Find more information about the object attributes of <code>author</code> <a href="${docsBaseUrl}context/author/#author-object-attributes" target=_blank>here</a>.`,
613
+ Find more information about the object attributes of <code>author</code> <a href="${docsBaseUrl}contexts/author/#author-object-attributes" target=_blank>here</a>.`,
576
614
  regex: /{{\s*?#if\s*?author\.image\s*?}}/g,
577
615
  helper: '{{#if author.image}}'
578
616
  },
@@ -583,7 +621,7 @@ let rules = {
583
621
  details: oneLineTrim`The <code>cover</code> attribute was replaced with <code>cover_image</code>.<br>
584
622
  Instead of <code>{{#if post.author.cover}}</code>, you need to use
585
623
  <code>{{#if post.primary_author.cover_image}}</code> or <code>{{#if post.authors.[#].cover_image}}</code>.<br>
586
- Find more information about the object attributes of <code>author</code> <a href="${docsBaseUrl}context/author/#author-object-attributes" target=_blank>here</a>.`,
624
+ Find more information about the object attributes of <code>author</code> <a href="${docsBaseUrl}contexts/author/#author-object-attributes" target=_blank>here</a>.`,
587
625
  regex: /{{\s*?#if\s*?post\.author\.cover\s*?}}/g,
588
626
  helper: '{{#if post.author.cover}}'
589
627
  },
@@ -594,7 +632,7 @@ let rules = {
594
632
  details: oneLineTrim`The <code>image</code> attribute was replaced with <code>profile_image</code>.<br>
595
633
  Instead of <code>{{#if post.author.image}}</code>, you need to use
596
634
  <code>{{#if post.primary_author.profile_image}}</code> or <code>{{#if post.authors.[#].profile_image}}</code>.<br>
597
- Find more information about the object attributes of <code>author</code> <a href="${docsBaseUrl}context/author/#author-object-attributes" target=_blank>here</a>.`,
635
+ Find more information about the object attributes of <code>author</code> <a href="${docsBaseUrl}contexts/author/#author-object-attributes" target=_blank>here</a>.`,
598
636
  regex: /{{\s*?#if\s*?post\.author\.image\s*?}}/g,
599
637
  helper: '{{#if post.author.image}}'
600
638
  },
@@ -685,8 +723,8 @@ let rules = {
685
723
  level: 'error',
686
724
  fatal: true,
687
725
  rule: 'Replace <code>{{[#].currency_symbol}}</code> with <code>{{price currency=currency}}</code>.',
688
- details: oneLineTrim`The hardcoded <code>currency_symbol</code> attribute is no longer supported in favour of passing the currency to updated <code>{{price}}</code> helper.
689
- Find more information about the updated <code>{{price}}</code> helper <a href="${docsBaseUrl}members/#the-price-helper" target=_blank>here</a>.`,
726
+ details: oneLineTrim`The <code>currency_symbol</code> attribute is no longer supported in favour of passing the currency to updated <code>{{price}}</code> helper.<br>
727
+ Find more information about the updated <code>{{price}}</code> helper <a href="${docsBaseUrl}helpers/price" target=_blank>here</a>.`,
690
728
  helper: '{{[#].currency_symbol}}',
691
729
  regex: /currency_symbol/g
692
730
  }