This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
# http://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.hbs]
|
||||
insert_final_newline = false
|
||||
|
||||
[{package,bower}.json]
|
||||
indent_size = 2
|
||||
|
||||
[*.{diff,md}]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.yml]
|
||||
indent_size = 2
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"component-structure": "flat",
|
||||
"component-class": "@glimmer/component",
|
||||
/**
|
||||
Ember CLI sends analytics information by default. The data is completely
|
||||
anonymous, but there are times when you might want to disable this behavior.
|
||||
|
||||
Setting `disableAnalytics` to true will prevent any data from being sent.
|
||||
*/
|
||||
"disableAnalytics": true
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
# unconventional js
|
||||
/blueprints/*/files/
|
||||
/vendor/
|
||||
|
||||
# compiled output
|
||||
/dist/
|
||||
/tmp/
|
||||
|
||||
# dependencies
|
||||
/bower_components/
|
||||
/node_modules/
|
||||
|
||||
# misc
|
||||
/coverage/
|
||||
.eslintcache
|
||||
|
||||
# ember-try
|
||||
/.node_modules.ember-try/
|
||||
/bower.json.ember-try
|
||||
/package.json.ember-try
|
||||
@@ -0,0 +1,70 @@
|
||||
/* eslint-env node */
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: '@babel/eslint-parser',
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
allowImportExportEverywhere: false,
|
||||
ecmaFeatures: {
|
||||
globalReturn: false,
|
||||
legacyDecorators: true,
|
||||
jsx: true
|
||||
},
|
||||
requireConfigFile: false,
|
||||
babelOptions: {
|
||||
plugins: [
|
||||
'@babel/plugin-proposal-class-properties',
|
||||
['@babel/plugin-proposal-decorators', {legacy: true}],
|
||||
'babel-plugin-transform-react-jsx'
|
||||
]
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
'ghost',
|
||||
'react'
|
||||
],
|
||||
extends: [
|
||||
'plugin:ghost/ember'
|
||||
],
|
||||
rules: {
|
||||
'ghost/filenames/match-exported-class': ['off'],
|
||||
// Enforce kebab-case (lowercase with hyphens) for all filenames
|
||||
'ghost/filenames/match-regex': ['error', '^[a-z0-9.-]+$', false],
|
||||
'no-shadow': ['error'],
|
||||
|
||||
// TODO: migrate away from accessing controller in routes
|
||||
'ghost/ember/no-controller-access-in-routes': 'off',
|
||||
|
||||
// TODO: enable once we're fully on octane 🏎
|
||||
'ghost/ember/no-assignment-of-untracked-properties-used-in-tracking-contexts': 'off',
|
||||
'ghost/ember/no-actions-hash': 'off',
|
||||
'ghost/ember/no-classic-classes': 'off',
|
||||
'ghost/ember/no-classic-components': 'off',
|
||||
'ghost/ember/require-tagless-components': 'off',
|
||||
'ghost/ember/no-component-lifecycle-hooks': 'off',
|
||||
|
||||
// disable linting of `this.get` until there's a reliable autofix
|
||||
'ghost/ember/use-ember-get-and-set': 'off',
|
||||
|
||||
// disable linting of mixins until we migrate away
|
||||
'ghost/ember/no-mixins': 'off',
|
||||
'ghost/ember/no-new-mixins': 'off',
|
||||
|
||||
'react/jsx-uses-react': 'error',
|
||||
'react/jsx-uses-vars': 'error'
|
||||
},
|
||||
overrides: [{
|
||||
files: 'tests/**/*.js',
|
||||
env: {
|
||||
embertest: true,
|
||||
mocha: true
|
||||
},
|
||||
extends: [
|
||||
'plugin:ghost/test'
|
||||
],
|
||||
rules: {
|
||||
'ghost/ember/no-invalid-debug-function-arguments': 'off',
|
||||
'ghost/mocha/no-setup-in-describe': 'off'
|
||||
}
|
||||
}]
|
||||
};
|
||||
@@ -0,0 +1,169 @@
|
||||
add|ember-template-lint|no-action|5|11|5|11|8eaebb48eca1563c6e0b18581df84ab59188d971|1746489600000|||app/components/gh-cm-editor.hbs
|
||||
add|ember-template-lint|no-passed-in-event-handlers|5|4|5|4|3a763e253744b070633bb8bd424b6c8e55f6b20a|1746489600000|||app/components/gh-cm-editor.hbs
|
||||
add|ember-template-lint|no-invalid-interactive|1|103|1|103|534029ab0ba1b74eff4a2f31c8b4dd9f1460316a|1746489600000|||app/components/gh-context-menu.hbs
|
||||
add|ember-template-lint|no-invalid-interactive|5|53|5|53|9647ef6afba919b2af04fe551b0fdf0fb63be849|1746489600000|||app/components/gh-context-menu.hbs
|
||||
add|ember-template-lint|no-action|5|18|5|18|0c80a75b2a80d404755333991c266c81c97c9cda|1746489600000|||app/components/gh-date-time-picker.hbs
|
||||
add|ember-template-lint|no-action|9|19|9|19|d12bcf1144bfb2fe70e7ab0f66836f1c6207a589|1746489600000|||app/components/gh-editor.hbs
|
||||
add|ember-template-lint|no-action|10|20|10|20|16d94650de2ffbe8ee3f2ce3ba5ca97a6304b739|1746489600000|||app/components/gh-editor.hbs
|
||||
add|ember-template-lint|no-action|11|17|11|17|30d64b1bf8990e2f84c52665690739fcc726e9f7|1746489600000|||app/components/gh-editor.hbs
|
||||
add|ember-template-lint|no-action|2|45|2|45|55b33496610b2f4ef6b9fe62713f4b4ddee37f19|1746489600000|||app/components/gh-fullscreen-modal.hbs
|
||||
add|ember-template-lint|no-action|10|29|10|29|ebbd89a393bcec7f537f2104ace2a6b1941a19a7|1746489600000|||app/components/gh-fullscreen-modal.hbs
|
||||
add|ember-template-lint|no-action|11|32|11|32|ab89b6f10c519be1271386203e9439d261eecd67|1746489600000|||app/components/gh-fullscreen-modal.hbs
|
||||
add|ember-template-lint|no-action|13|36|13|36|3776877637b49b65deef537c68ae490b2fb081a9|1746489600000|||app/components/gh-fullscreen-modal.hbs
|
||||
add|ember-template-lint|no-invalid-interactive|2|45|2|45|918fdec8490009c6197091e319d6ec52ee95b983|1746489600000|||app/components/gh-fullscreen-modal.hbs
|
||||
add|ember-template-lint|link-href-attributes|8|8|8|8|2078c6e43d1e5548ae745b7ac8cb736f7d2aaf68|1746489600000|||app/components/gh-image-uploader-with-preview.hbs
|
||||
add|ember-template-lint|no-invalid-interactive|8|60|8|60|2078c6e43d1e5548ae745b7ac8cb736f7d2aaf68|1746489600000|||app/components/gh-image-uploader-with-preview.hbs
|
||||
add|ember-template-lint|require-valid-alt-text|3|13|3|13|079fc89fa5c7c47f6b0b219820cdda3819c44e26|1746489600000|||app/components/gh-image-uploader-with-preview.hbs
|
||||
add|ember-template-lint|no-action|12|58|12|58|031d04576149d3034549aa3d14bc26da704cd70c|1746489600000|||app/components/gh-image-uploader.hbs
|
||||
add|ember-template-lint|no-action|17|75|17|75|bbc5d9a459cd07e56d8c79dde619775b89d7cc89|1746489600000|||app/components/gh-image-uploader.hbs
|
||||
add|ember-template-lint|no-action|22|52|22|52|b1bd53a513ad82434d5a0a9a96441a45d416d7ad|1746489600000|||app/components/gh-image-uploader.hbs
|
||||
add|ember-template-lint|no-action|31|16|31|16|4c64ddeaf795ee831d0d7346667a305814ca9855|1746489600000|||app/components/gh-image-uploader.hbs
|
||||
add|ember-template-lint|no-action|32|15|32|15|b1bd53a513ad82434d5a0a9a96441a45d416d7ad|1746489600000|||app/components/gh-image-uploader.hbs
|
||||
add|ember-template-lint|no-invalid-interactive|22|52|22|52|74dea739bef284d6557987ff3da53fa1278030e2|1746489600000|||app/components/gh-image-uploader.hbs
|
||||
add|ember-template-lint|no-action|10|16|10|16|8d8dd8c2cb5f9910c2de5ca6acc76ee4262a876e|1746489600000|||app/components/gh-members-import-mapping-input.hbs
|
||||
add|ember-template-lint|no-action|9|36|9|36|05358b6ec6e9afbaa47416266c49f40a3ffb4490|1746489600000|||app/components/gh-members-import-table.hbs
|
||||
add|ember-template-lint|no-action|10|36|10|36|c5ce93cf577ec47970f715ed83ed11a63adb7a63|1746489600000|||app/components/gh-members-import-table.hbs
|
||||
add|ember-template-lint|no-redundant-role|6|20|6|20|bc4fbabe3d468440d8a2c70e92cec991caca41e2|1746489600000|||app/components/gh-post-bookmark.hbs
|
||||
add|ember-template-lint|no-redundant-role|14|64|14|64|ce988c0098c6e4845fc3307eb523b8b1687a3ecf|1746489600000|||app/components/gh-post-bookmark.hbs
|
||||
add|ember-template-lint|no-action|36|43|36|43|66cebfc8448eced0bccf3faa57b4af0cd633e65e|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|37|47|37|47|70f3e9aa9a9aa52142c4e0c015a8f173d12b05ed|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|56|41|56|41|43cc094c1a6b50ecd24c8af325dfdfcac863bb14|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|57|41|57|41|e7f429eefd04a55d4ef1875fe601da1b69964364|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|87|50|87|50|0e827c1770073f988535ceb9d6c49808a153d896|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|104|39|104|39|5cbc9d2abf29e108bfc207579df1206485e2a74d|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|105|43|105|43|1f2dd4961e9757ede9706bb0aa9b8baf9c49b22b|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|116|132|116|132|90dbb84741c72aa86a601e1cb95c066bd53898bd|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|123|46|123|46|56ca074eb0236b8becc4084423056a8ffa4453e9|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|158|73|158|73|b07bf9d50af5f00861571521b150cf6504b90704|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|172|56|172|56|38755451c044c56040684339301c21b2aab49ecb|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|190|50|190|50|9f1c3c88187e5db774f97704269e372f8331eba5|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|196|50|196|50|97acfb2045b33a97621258596b631362ad4c56d5|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|202|51|202|51|358336432fcb413c522c5f1ff3062a68ef9f449f|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|208|50|208|50|78a45cbb7eafdda134d96f6035b0026f339e75ad|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|214|50|214|50|693211baf08c011e77284b483c30e28ecaf63520|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|223|105|223|105|de5b68b49193c72f85c0e478f151a00180321d91|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|236|167|236|167|0145b67f0faef0aad141c6a4269c35c6ef8f0a47|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|601|163|601|163|0145b67f0faef0aad141c6a4269c35c6ef8f0a47|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|607|34|607|34|3d44a2ad21d60d18bed6f79265caa4887361aaa4|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|616|47|616|47|67551960e57b2ad06d24289e0d6f256dc5635cae|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|617|51|617|51|50e3029f4c845049ff10130cf0dc2ea06c7a3d2d|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|632|47|632|47|0d852775b4028d61c2c91b5d821c79de3cdbd1de|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|633|51|633|51|194439ec4cb10e176eecb1f55c2995c00f6c67b4|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|647|47|647|47|3be93048908ab43d74726c3983982fcb9a3570e3|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|648|51|648|51|40871d259cc307345b8096e4215ae58e5886b724|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|674|166|674|166|0145b67f0faef0aad141c6a4269c35c6ef8f0a47|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|681|34|681|34|3d44a2ad21d60d18bed6f79265caa4887361aaa4|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|686|44|686|44|d2abbb4bb55b6cb1131ca0bc56c31632b32b980c|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|687|44|687|44|3ede83bd64d206d665edb4f1b73f03b4122edfad|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|697|47|697|47|13b7064b520a924d9e57a4a680621ce979d23923|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|698|51|698|51|b0fc38b818f4ca2613684588e4122e12da0090b9|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|713|47|713|47|f34f084dc72c079be9ba3eb93c255bd8853612bd|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|714|51|714|51|c5c09d001fa96a623649cc88ef13b3c6c164f05b|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|742|167|742|167|0145b67f0faef0aad141c6a4269c35c6ef8f0a47|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|748|34|748|34|3d44a2ad21d60d18bed6f79265caa4887361aaa4|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|753|44|753|44|32101c0834d9e8d2caa87dee9cb1a92fe633cfde|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|754|44|754|44|c8a82286cfe4220cb2d8a059b1daf7f6c8d54cf5|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|764|47|764|47|31800fb83f1797de5d91b7007c48432cf2fae803|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|765|51|765|51|5ce0788874489341385f031810e9f7f62724aaf8|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|778|47|778|47|1cbd7d922a9d202772a9dca214d97bed18777d6e|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|779|51|779|51|16fb2c7c87dc282c6ee49a032aaaf22628b88084|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|810|168|810|168|0145b67f0faef0aad141c6a4269c35c6ef8f0a47|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|816|34|816|34|3d44a2ad21d60d18bed6f79265caa4887361aaa4|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|823|50|823|50|d03e7bbf2b6de94b08fae1c33bd5a2f16874e03a|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|825|48|825|48|bca167f0250b40d56ff1ed40ec21bc88f8e27ec3|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|836|50|836|50|93ae447fb1054de4dbe501fc60167a6ff3273687|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|838|48|838|48|ecb9ed2577be7ea0300563f22d8e9fde2fed12a6|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-passed-in-event-handlers|36|36|36|36|fed655605208b290a18b9f7da51a6cf7b40c0e9a|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-passed-in-event-handlers|104|32|104|32|187e85e14470b453a1e6df8c5f18549d11c441a0|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-passed-in-event-handlers|616|40|616|40|f290a04fd56f613d80d61244d161631f630185a8|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-passed-in-event-handlers|632|40|632|40|c1af88109f705acc93fab4ee7b4d89096136ffe1|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-passed-in-event-handlers|647|40|647|40|852cbb2c63e19a28d700d3b18e6f887edef303fa|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-passed-in-event-handlers|697|40|697|40|58185c92e8b6261ea1483a70296d9fa837d3f2f5|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-passed-in-event-handlers|713|40|713|40|c4353acd715c396564c69389edd0a52246d3b966|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-passed-in-event-handlers|764|40|764|40|29f26e46559dc40d9724a05b7516cb52f1481aaa|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-passed-in-event-handlers|778|40|778|40|42e0617f832585eaa056c9c66dffe1a126318faf|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-passed-in-event-handlers|823|40|823|40|d1dccfeee5b103fac3b14d5b4567bc65ee08fd5a|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-passed-in-event-handlers|836|40|836|40|055c4b70aa8daba6b97077eefa95ebed7f7f5315|1746489600000|||app/components/gh-post-settings-menu.hbs
|
||||
add|ember-template-lint|no-action|4|14|4|14|c8f6146f9286ec1b289e28983ab446386f390f01|1746489600000|||app/components/gh-psm-authors-input.hbs
|
||||
add|ember-template-lint|no-action|9|24|9|24|2e612be5e2b8e0b792f951c3c75b2f71df880365|1746489600000|||app/components/gh-psm-template-select.hbs
|
||||
add|ember-template-lint|no-action|7|16|7|16|86819b20fcc42bc1d4607519c1d45532ee337884|1746489600000|||app/components/gh-psm-visibility-input.hbs
|
||||
add|ember-template-lint|no-autofocus-attribute|13|16|13|16|fa0ffb960072633b72117849e3927673be0059af|1746489600000|||app/components/gh-search-input.hbs
|
||||
add|ember-template-lint|require-iframe-title|1|0|1|0|956ab219134ac63aec3fd2d35582076c21631b75|1746489600000|||app/components/gh-site-iframe.hbs
|
||||
add|ember-template-lint|no-yield-only|1|0|1|0|a5fa6e8c1e0f03fb31b5cd17770e4368d44932e4|1746489600000|||app/components/gh-text-input.hbs
|
||||
add|ember-template-lint|no-action|1|51|1|51|674a942db62e014b25156b02d014f6c63b2f1cb2|1746489600000|||app/components/gh-theme-error-li.hbs
|
||||
add|ember-template-lint|no-action|2|11|2|11|2b1317e72b94ec31bf2700acc3f041e6be974b72|1746489600000|||app/components/gh-uploader.hbs
|
||||
add|ember-template-lint|no-action|7|13|7|13|add4b57d48167f809045245535d45bedb00cb753|1746489600000|||app/components/gh-uploader.hbs
|
||||
add|ember-template-lint|no-action|8|22|8|22|ce1eba2b791c0f120baf10871fd3949c1b9f6a79|1746489600000|||app/components/gh-uploader.hbs
|
||||
add|ember-template-lint|no-action|9|22|9|22|0209f233628ec084b87894b4be9073c29f2a4f47|1746489600000|||app/components/gh-uploader.hbs
|
||||
add|ember-template-lint|no-passed-in-event-handlers|4|4|4|4|3dedc53191768d81765eac4a7a049a9aba7df442|1746489600000|||app/components/gh-url-input.hbs
|
||||
add|ember-template-lint|no-action|1|71|1|71|2e6351f546807d88cc8eb9dbe8baa149468b5cb9|1746489600000|||app/components/gh-view-title.hbs
|
||||
add|ember-template-lint|no-invalid-role|1|0|1|0|3e651d38e0110e1be20e5082075db1b879b59a36|1746489600000|||app/components/gh-view-title.hbs
|
||||
add|ember-template-lint|no-action|5|50|5|50|141d456b03124abca146e58e4ae15825fdd040bb|1746489600000|||app/components/modal-impersonate-member.hbs
|
||||
add|ember-template-lint|no-action|5|74|5|74|d465b362b15b90cf42a093e72895155f49cdf6f2|1746489600000|||app/components/modal-impersonate-member.hbs
|
||||
add|ember-template-lint|no-action|43|57|43|57|141d456b03124abca146e58e4ae15825fdd040bb|1746489600000|||app/components/modal-import-members.hbs
|
||||
add|ember-template-lint|no-action|50|56|50|56|190532b9954beb4e7e9e0f1c51d170e64d4a5315|1746489600000|||app/components/modal-import-members.hbs
|
||||
add|ember-template-lint|no-action|56|34|56|34|ff609af61e9159dfc5386543d559f6d217d39531|1746489600000|||app/components/modal-import-members.hbs
|
||||
add|ember-template-lint|no-action|57|29|57|29|9a0a72738b6e1a4f46415b2328c37d1814562717|1746489600000|||app/components/modal-import-members.hbs
|
||||
add|ember-template-lint|no-action|110|89|110|89|141d456b03124abca146e58e4ae15825fdd040bb|1746489600000|||app/components/modal-import-members.hbs
|
||||
add|ember-template-lint|no-action|116|91|116|91|031d04576149d3034549aa3d14bc26da704cd70c|1746489600000|||app/components/modal-import-members.hbs
|
||||
add|ember-template-lint|no-action|119|116|119|116|7e581bf2ffd5254ae851201e1f23cb9eaa9b198b|1746489600000|||app/components/modal-import-members.hbs
|
||||
add|ember-template-lint|no-action|129|120|129|120|031d04576149d3034549aa3d14bc26da704cd70c|1746489600000|||app/components/modal-import-members.hbs
|
||||
add|ember-template-lint|no-action|132|103|132|103|7e581bf2ffd5254ae851201e1f23cb9eaa9b198b|1746489600000|||app/components/modal-import-members.hbs
|
||||
add|ember-template-lint|no-action|143|112|143|112|031d04576149d3034549aa3d14bc26da704cd70c|1746489600000|||app/components/modal-import-members.hbs
|
||||
add|ember-template-lint|no-action|147|110|147|110|141d456b03124abca146e58e4ae15825fdd040bb|1746489600000|||app/components/modal-import-members.hbs
|
||||
add|ember-template-lint|no-action|153|97|153|97|141d456b03124abca146e58e4ae15825fdd040bb|1746489600000|||app/components/modal-import-members.hbs
|
||||
add|ember-template-lint|no-action|156|112|156|112|031d04576149d3034549aa3d14bc26da704cd70c|1746489600000|||app/components/modal-import-members.hbs
|
||||
add|ember-template-lint|no-action|160|99|160|99|031d04576149d3034549aa3d14bc26da704cd70c|1746489600000|||app/components/modal-import-members.hbs
|
||||
add|ember-template-lint|no-action|163|110|163|110|141d456b03124abca146e58e4ae15825fdd040bb|1746489600000|||app/components/modal-import-members.hbs
|
||||
add|ember-template-lint|no-action|171|91|171|91|031d04576149d3034549aa3d14bc26da704cd70c|1746489600000|||app/components/modal-import-members.hbs
|
||||
add|ember-template-lint|no-action|174|102|174|102|141d456b03124abca146e58e4ae15825fdd040bb|1746489600000|||app/components/modal-import-members.hbs
|
||||
add|ember-template-lint|no-action|181|95|181|95|031d04576149d3034549aa3d14bc26da704cd70c|1746489600000|||app/components/modal-import-members.hbs
|
||||
add|ember-template-lint|no-action|185|102|185|102|141d456b03124abca146e58e4ae15825fdd040bb|1746489600000|||app/components/modal-import-members.hbs
|
||||
add|ember-template-lint|no-invalid-interactive|28|24|28|24|42a29ae16e22270f0590c9ce5caa7bfec541ca0b|1746489600000|||app/components/modal-member-tier.hbs
|
||||
add|ember-template-lint|no-action|5|57|5|57|141d456b03124abca146e58e4ae15825fdd040bb|1746489600000|||app/components/modal-members-label-form.hbs
|
||||
add|ember-template-lint|no-action|14|45|14|45|141d456b03124abca146e58e4ae15825fdd040bb|1746489600000|||app/components/modal-members-label-form.hbs
|
||||
add|ember-template-lint|no-action|23|59|23|59|141d456b03124abca146e58e4ae15825fdd040bb|1746489600000|||app/components/modal-members-label-form.hbs
|
||||
add|ember-template-lint|no-action|23|83|23|83|d465b362b15b90cf42a093e72895155f49cdf6f2|1746489600000|||app/components/modal-members-label-form.hbs
|
||||
add|ember-template-lint|no-action|34|31|34|31|a8ad062b8379233b7970fe5ea74296fdf5011567|1746489600000|||app/components/modal-members-label-form.hbs
|
||||
add|ember-template-lint|no-action|47|82|47|82|141d456b03124abca146e58e4ae15825fdd040bb|1746489600000|||app/components/modal-members-label-form.hbs
|
||||
add|ember-template-lint|no-action|49|16|49|16|d465b362b15b90cf42a093e72895155f49cdf6f2|1746489600000|||app/components/modal-members-label-form.hbs
|
||||
add|ember-template-lint|no-action|55|113|55|113|c259009ff744c2e9f7bb273e0eb3a3879036ba85|1746489600000|||app/components/modal-members-label-form.hbs
|
||||
add|ember-template-lint|no-redundant-role|85|24|85|24|ce988c0098c6e4845fc3307eb523b8b1687a3ecf|1746489600000|||app/components/modal-post-success.hbs
|
||||
add|ember-template-lint|no-redundant-role|136|24|136|24|ce988c0098c6e4845fc3307eb523b8b1687a3ecf|1746489600000|||app/components/modal-post-success.hbs
|
||||
add|ember-template-lint|no-action|4|53|4|53|141d456b03124abca146e58e4ae15825fdd040bb|1746489600000|||app/components/modal-unsubscribe-members.hbs
|
||||
add|ember-template-lint|no-action|50|89|50|89|141d456b03124abca146e58e4ae15825fdd040bb|1746489600000|||app/components/modal-unsubscribe-members.hbs
|
||||
add|ember-template-lint|no-action|54|71|54|71|141d456b03124abca146e58e4ae15825fdd040bb|1746489600000|||app/components/modal-unsubscribe-members.hbs
|
||||
add|ember-template-lint|require-valid-alt-text|4|12|4|12|8369d1b06deac93e8c8e05444670c15182aea434|1746489600000|||app/templates/application-error.hbs
|
||||
add|ember-template-lint|no-redundant-role|91|40|91|40|9d2eded16257516b455504637aab4057b3c0c9ef|1746489600000|||app/templates/mentions.hbs
|
||||
add|ember-template-lint|no-redundant-role|113|20|113|20|0418319e2dce98b674dbb6657d185f465d21f87d|1746489600000|||app/templates/mentions.hbs
|
||||
add|ember-template-lint|no-action|20|31|20|31|3aa834e53af871a821ebb1b47f7a04f925c2b593|1746489600000|||app/templates/setup.hbs
|
||||
add|ember-template-lint|no-action|21|35|21|35|ae65e93ed2b79a307e0eba50b154fe84ee1ae210|1746489600000|||app/templates/setup.hbs
|
||||
add|ember-template-lint|no-action|37|31|37|31|eefdee43411bdd45c2786b9e826e6b550fd6212d|1746489600000|||app/templates/setup.hbs
|
||||
add|ember-template-lint|no-action|38|35|38|35|a8e4bd4c57a8f2df65749b0cfa4349da22b2a0aa|1746489600000|||app/templates/setup.hbs
|
||||
add|ember-template-lint|no-action|55|31|55|31|1709109776bf3fc47aaecc21e6d3ec8a0489ad6b|1746489600000|||app/templates/setup.hbs
|
||||
add|ember-template-lint|no-action|56|35|56|35|8000241a81189d3876d264331ec0c9b6476df076|1746489600000|||app/templates/setup.hbs
|
||||
add|ember-template-lint|no-action|73|31|73|31|6756e119daad4aa143724e9b56a6aef744d65251|1746489600000|||app/templates/setup.hbs
|
||||
add|ember-template-lint|no-action|74|35|74|35|26b1d89c0e5bbcd629a215903c0819d35f798aa6|1746489600000|||app/templates/setup.hbs
|
||||
add|ember-template-lint|no-passed-in-event-handlers|20|24|20|24|f0b7babba7593639d68dadae2f19e7144dd8c63e|1746489600000|||app/templates/setup.hbs
|
||||
add|ember-template-lint|no-passed-in-event-handlers|37|24|37|24|2692530760cb1f7156dcf9570aacf0f8baca2770|1746489600000|||app/templates/setup.hbs
|
||||
add|ember-template-lint|no-passed-in-event-handlers|55|24|55|24|ce0d7e2e732b22ce3643fb9b1ac6ecef07c275ff|1746489600000|||app/templates/setup.hbs
|
||||
add|ember-template-lint|no-passed-in-event-handlers|73|24|73|24|80681fcec2258c3d81a231bbd635aa1f03ff1452|1746489600000|||app/templates/setup.hbs
|
||||
add|ember-template-lint|no-action|6|108|6|108|ccc38f66549f9baedaa3b9943ae6634ea8f99e69|1746489600000|||app/templates/tags.hbs
|
||||
add|ember-template-lint|no-action|7|110|7|110|c3819ce2b6989e8596be570ed0c9fb82b5012521|1746489600000|||app/templates/tags.hbs
|
||||
add|ember-template-lint|require-valid-alt-text|15|32|15|32|80c1ce6724481312363dc4e1db42bf28b41909f2|1746489600000|||app/templates/whatsnew.hbs
|
||||
add|ember-template-lint|no-invalid-interactive|8|51|8|51|66a27dbed218d15e49e91c72a93215ad0d90f778|1746489600000|||app/components/gh-power-select/trigger.hbs
|
||||
add|ember-template-lint|no-yield-only|1|0|1|0|a5fa6e8c1e0f03fb31b5cd17770e4368d44932e4|1746489600000|||app/components/gh-token-input/label-token.hbs
|
||||
add|ember-template-lint|no-yield-only|1|0|1|0|a5fa6e8c1e0f03fb31b5cd17770e4368d44932e4|1746489600000|||app/components/gh-token-input/tag-token.hbs
|
||||
add|ember-template-lint|no-action|8|19|8|19|73ac7d3892fcbcf15c3d5c44fca14dd21016daea|1746489600000|||app/components/gh-token-input/trigger.hbs
|
||||
add|ember-template-lint|no-positive-tabindex|46|8|46|8|6118264a9a0599fab6ad4da7264c1bfffa88687e|1746489600000|||app/components/gh-token-input/trigger.hbs
|
||||
add|ember-template-lint|require-iframe-title|27|20|27|20|94e58d11848d5613900c2188ba63dac41f4c03bb|1746489600000|||app/components/modals/email-preview.hbs
|
||||
add|ember-template-lint|require-iframe-title|42|16|42|16|a3292b469dc37f2f4791e7f224b0b65c8ecf5d18|1746489600000|||app/components/modals/email-preview.hbs
|
||||
add|ember-template-lint|no-autofocus-attribute|21|20|21|20|942419d05c04ded6716f09faecd6b1ab55418121|1746489600000|||app/components/modals/new-custom-integration.hbs
|
||||
add|ember-template-lint|no-invalid-interactive|2|37|2|37|e21ba31f54b631a428c28a1c9f88d0dc66f2f5fc|1746489600000|||app/components/modals/search.hbs
|
||||
add|ember-template-lint|no-redundant-role|11|20|11|20|bc4fbabe3d468440d8a2c70e92cec991caca41e2|1746489600000|||app/components/dashboard/onboarding/share-modal.hbs
|
||||
remove|ember-template-lint|no-action|6|108|6|108|ccc38f66549f9baedaa3b9943ae6634ea8f99e69|1746489600000|||app/templates/tags.hbs
|
||||
remove|ember-template-lint|no-action|7|110|7|110|c3819ce2b6989e8596be570ed0c9fb82b5012521|1746489600000|||app/templates/tags.hbs
|
||||
remove|ember-template-lint|require-valid-alt-text|4|12|4|12|8369d1b06deac93e8c8e05444670c15182aea434|1746489600000|||app/templates/application-error.hbs
|
||||
remove|ember-template-lint|no-action|1|71|1|71|2e6351f546807d88cc8eb9dbe8baa149468b5cb9|1746489600000|||app/components/gh-view-title.hbs
|
||||
remove|ember-template-lint|no-invalid-role|1|0|1|0|3e651d38e0110e1be20e5082075db1b879b59a36|1746489600000|||app/components/gh-view-title.hbs
|
||||
add|ember-template-lint|no-yield-only|1|0|1|0|a5fa6e8c1e0f03fb31b5cd17770e4368d44932e4|1771200000000|||app/components/gh-view-title.hbs
|
||||
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
'ember-template-lint': {
|
||||
daysToDecay: null
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
module.exports = {
|
||||
extends: "recommended",
|
||||
|
||||
rules: {
|
||||
'no-forbidden-elements': ['meta', 'html', 'script'],
|
||||
'no-implicit-this': {allow: ['noop', 'now', 'site-icon-style']},
|
||||
'no-inline-styles': false,
|
||||
'no-duplicate-landmark-elements': false,
|
||||
'no-pointer-down-event-binding': false,
|
||||
'no-triple-curlies': false
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"ignore_dirs": ["tmp", "dist"]
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
# Ghost-Admin
|
||||
|
||||
This is the home of the Ember.js-based Admin app that ships with [Ghost](https://github.com/tryghost/ghost).
|
||||
|
||||
## Test
|
||||
|
||||
### Running tests in the browser
|
||||
|
||||
Run all tests in the browser by running `pnpm dev` in the Ghost monorepo and visiting http://localhost:4200/tests. The code is hotloaded on change and you can filter which tests to run.
|
||||
|
||||
[Testing public documentation](https://ghost.notion.site/Testing-Ember-560cec6700fc4d37a58b3ba9febb4b4b)
|
||||
|
||||
---
|
||||
|
||||
Tip: You can use `this.timeout(0); await this.pauseTest();` in your tests to temporarily pause the execution of browser tests. Use the browser console to inspect and debug the DOM, then resume tests by running `resumeTest()` directly in the browser console ([docs](https://guides.emberjs.com/v3.28.0/testing/testing-application/#toc_debugging-your-tests))
|
||||
|
||||
|
||||
### Running tests in the CLI
|
||||
|
||||
To build and run tests in the CLI, you can use:
|
||||
|
||||
```bash
|
||||
TZ=UTC pnpm test
|
||||
```
|
||||
_Note the `TZ=UTC` environment variable which is currently required to get tests working if your system timezone doesn't match UTC._
|
||||
|
||||
---
|
||||
|
||||
However, this is very slow when writing tests, as it requires the app to be rebuilt on every change. Instead, create a separate watching build with:
|
||||
|
||||
```bash
|
||||
pnpm build --environment=test -w -o="dist-test"
|
||||
```
|
||||
|
||||
Then run tests with:
|
||||
|
||||
```bash
|
||||
TZ=UTC pnpm test 1 --reporter dot --path="dist-test"
|
||||
```
|
||||
|
||||
The `--reporter dot` shows a dot (`.`) for every successful test, and `F` for every failed test. It renders the output of the failed tests only.
|
||||
|
||||
---
|
||||
|
||||
To run a specific test file:
|
||||
```bash
|
||||
TZ=UTC pnpm test 1 --reporter dot --path="dist-test" -mp=tests/unit/helpers/gh-count-characters-test.js
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
To have a full list of the available options, run
|
||||
```bash
|
||||
ember exam --help
|
||||
```
|
||||
|
||||
# Copyright & License
|
||||
|
||||
Copyright (c) 2013-2026 Ghost Foundation - Released under the [MIT license](LICENSE). Ghost and the Ghost Logo are trademarks of Ghost Foundation Ltd. Please see our [trademark policy](https://ghost.org/trademark/) for info on acceptable usage.
|
||||
@@ -0,0 +1,30 @@
|
||||
# Ghost Admin App
|
||||
|
||||
Ember.js application used as a client-side admin for the [Ghost](http://ghost.org) blogging platform. This readme is a work in progress guide aimed at explaining the specific nuances of the Ghost Ember app to contributors whose main focus is on this side of things.
|
||||
|
||||
|
||||
## CSS
|
||||
|
||||
We use pure CSS, which is pre-processed for backwards compatibility by [Myth](http://myth.io). We do not follow any strict CSS framework, however our general style is pretty similar to BEM.
|
||||
|
||||
Styles are primarily broken up into 4 main categories:
|
||||
|
||||
* **Patterns** - are base level visual styles for HTML elements (eg. Buttons)
|
||||
* **Components** - are groups of patterns used to create a UI component (eg. Modals)
|
||||
* **Layouts** - are groups of components used to create application screens (eg. Settings)
|
||||
|
||||
All of these separate files are subsequently imported and compiled in `app.css`.
|
||||
|
||||
|
||||
## Front End Standards
|
||||
|
||||
* 4 spaces for HTML & CSS indentation. Never tabs.
|
||||
* Double quotes only, never single quotes.
|
||||
* Use tags and elements appropriate for an HTML5 doctype (including self-closing tags)
|
||||
* Adhere to the [Recess CSS](http://markdotto.com/2011/11/29/css-property-order/) property order.
|
||||
* Always a space after a property's colon (.e.g, display: block; and not display:block;).
|
||||
* End all lines with a semi-colon.
|
||||
* For multiple, comma-separated selectors, place each selector on its own line.
|
||||
* Use js- prefixed classes for JavaScript hooks into the DOM, and never use these in CSS as per [Slightly Obtrusive JavaScript](http://ozmm.org/posts/slightly_obtrusive_javascript.html)
|
||||
* Avoid over-nesting CSS. Never nest more than 3 levels deep.
|
||||
* Use comments to explain "why" not "what" (Good: This requires a z-index in order to appear above mobile navigation. Bad: This is a thing which is always on top!)
|
||||
Executable
+54
@@ -0,0 +1,54 @@
|
||||
import 'ghost-admin/utils/link-component';
|
||||
import 'ghost-admin/utils/route';
|
||||
import Application from '@ember/application';
|
||||
import Resolver from 'ember-resolver';
|
||||
import config from 'ghost-admin/config/environment';
|
||||
import loadInitializers from 'ember-load-initializers';
|
||||
import moment from 'moment-timezone';
|
||||
import {registerWarnHandler} from '@ember/debug';
|
||||
|
||||
moment.updateLocale('en', {
|
||||
relativeTime: {
|
||||
m: '1 minute'
|
||||
}
|
||||
});
|
||||
|
||||
const rootElement = document.getElementById('ember-app');
|
||||
|
||||
const App = Application.extend({
|
||||
Resolver,
|
||||
modulePrefix: config.modulePrefix,
|
||||
podModulePrefix: config.podModulePrefix,
|
||||
|
||||
// eslint-disable-next-line
|
||||
customEvents: {
|
||||
touchstart: null,
|
||||
touchmove: null,
|
||||
touchend: null,
|
||||
touchcancel: null
|
||||
},
|
||||
|
||||
...(rootElement ? {
|
||||
rootElement: '#ember-app'
|
||||
} : {})
|
||||
});
|
||||
|
||||
// TODO: remove once the validations refactor is complete
|
||||
// eslint-disable-next-line
|
||||
registerWarnHandler((message, options, next) => {
|
||||
let skip = [
|
||||
'ds.errors.add',
|
||||
'ds.errors.remove',
|
||||
'ds.errors.clear'
|
||||
];
|
||||
|
||||
if (skip.includes(options.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
next(message, options);
|
||||
});
|
||||
|
||||
loadInitializers(App, config.modulePrefix);
|
||||
|
||||
export default App;
|
||||
@@ -0,0 +1,52 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="no-js" lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
|
||||
<title>Ghost</title>
|
||||
|
||||
{{content-for "head"}}
|
||||
|
||||
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1, minimal-ui, viewport-fit=cover" />
|
||||
<meta name="pinterest" content="nopin" />
|
||||
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="application-name" content="Ghost" />
|
||||
<meta name="apple-mobile-web-app-title" content="Ghost" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
||||
|
||||
<link rel="shortcut icon" href="assets/img/favicon.ico" />
|
||||
<link rel="apple-touch-icon" href="assets/img/apple-touch-icon.png" />
|
||||
|
||||
<!-- variables that we don't want postcss-custom-properties to remove -->
|
||||
<style>
|
||||
:root {
|
||||
--editor-sidebar-width: 0px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<link integrity="" rel="stylesheet" href="{{rootURL}}assets/vendor.css">
|
||||
<link integrity="" rel="stylesheet" href="{{rootURL}}assets/ghost.css" title="light">
|
||||
|
||||
{{content-for "head-footer"}}
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="ember-load-indicator">
|
||||
<div class="gh-loading-content">
|
||||
<video width="100" height="100" loop autoplay muted playsinline preload="metadata" style="width: 100px; height: 100px;">
|
||||
<source src="assets/videos/logo-loader.mp4" type="video/mp4" />
|
||||
<div class="gh-loading-spinner"></div>
|
||||
</video>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{content-for "body"}}
|
||||
|
||||
{{content-for "body-footer"}}
|
||||
|
||||
<script src="{{rootURL}}assets/vendor.js"></script>
|
||||
<script src="{{rootURL}}assets/ghost.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,70 @@
|
||||
import EmberRouter from '@ember/routing/router';
|
||||
import config from 'ghost-admin/config/environment';
|
||||
import ghostPaths from 'ghost-admin/utils/ghost-paths';
|
||||
|
||||
const Router = EmberRouter.extend({
|
||||
location: config.locationType, // use HTML5 History API instead of hash-tag based URLs
|
||||
rootURL: ghostPaths().adminRoot // admin interface lives under sub-directory /ghost
|
||||
});
|
||||
|
||||
// eslint-disable-next-line array-callback-return
|
||||
Router.map(function () {
|
||||
this.route('home', {path: '/'});
|
||||
|
||||
this.route('setup');
|
||||
this.route('setup.done', {path: '/setup/done'});
|
||||
|
||||
this.route('signin');
|
||||
this.route('signin-verify', {path: '/signin/verify'});
|
||||
this.route('signout');
|
||||
this.route('signup', {path: '/signup/:token'});
|
||||
this.route('reset', {path: '/reset/:token'});
|
||||
|
||||
this.route('site');
|
||||
this.route('dashboard');
|
||||
this.route('launch');
|
||||
|
||||
this.route('pro', function () {
|
||||
this.route('pro-sub', {path: '/*sub'});
|
||||
});
|
||||
|
||||
this.route('posts');
|
||||
this.route('posts.debug', {path: '/posts/analytics/:post_id/debug'});
|
||||
this.route('restore-posts', {path: '/restore'});
|
||||
|
||||
this.route('pages');
|
||||
|
||||
this.route('lexical-editor', {path: 'editor'}, function () {
|
||||
this.route('new', {path: ':type'});
|
||||
this.route('edit', {path: ':type/:post_id'});
|
||||
});
|
||||
|
||||
this.route('tag.new', {path: '/tags/new'});
|
||||
this.route('tag', {path: '/tags/:tag_slug'});
|
||||
|
||||
this.route('explore', function () {
|
||||
// actual Ember route, not rendered in iframe
|
||||
this.route('connect');
|
||||
// iframe sub pages, used for categories
|
||||
this.route('explore-sub', {path: '/*sub'}, function () {
|
||||
// needed to allow search to work, as it uses URL
|
||||
// params for search queries. They don't need to
|
||||
// be visible, but may not be cut off.
|
||||
this.route('explore-query', {path: '/*query'});
|
||||
});
|
||||
});
|
||||
|
||||
this.route('migrate', function () {
|
||||
this.route('migrate', {path: '/*platform'});
|
||||
});
|
||||
|
||||
this.route('member.new', {path: '/members/new'});
|
||||
this.route('member', {path: '/members/:member_id'});
|
||||
this.route('members-activity');
|
||||
|
||||
this.route('react-fallback', {path: '/*path'});
|
||||
|
||||
this.route('designsandbox');
|
||||
});
|
||||
|
||||
export default Router;
|
||||
@@ -0,0 +1,21 @@
|
||||
export default function () {
|
||||
this.transition(
|
||||
this.hasClass('fullscreen-modal-container'),
|
||||
this.toValue(true),
|
||||
this.use('fade', {duration: 150}),
|
||||
this.reverse('fade', {duration: 150})
|
||||
);
|
||||
|
||||
this.transition(
|
||||
this.hasClass('fade-transition'),
|
||||
this.use('crossFade', {duration: 100})
|
||||
);
|
||||
|
||||
// TODO: Maybe animate with explode. gh-unsplash-window should ideally slide in from bottom to top of screen
|
||||
// this.transition(
|
||||
// this.hasClass('gh-unsplash-window'),
|
||||
// this.toValue(true),
|
||||
// this.use('toUp', {duration: 500}),
|
||||
// this.reverse('toDown', {duration: 500})
|
||||
// );
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
self.deprecationWorkflow = self.deprecationWorkflow || {};
|
||||
self.deprecationWorkflow.config = {
|
||||
workflow: [
|
||||
// remove once ember-drag-drop removes usage of Component#sendAction
|
||||
// https://github.com/mharris717/ember-drag-drop/issues/155
|
||||
{handler: 'silence', matchId: 'ember-component.send-action'},
|
||||
|
||||
// remove once liquid-fire and liquid-wormhole remove uses of `this.$()`
|
||||
{handler: 'silence', matchId: 'ember-views.curly-components.jquery-element'}
|
||||
]
|
||||
};
|
||||
@@ -0,0 +1,82 @@
|
||||
/* eslint-env node */
|
||||
'use strict';
|
||||
|
||||
module.exports = function (environment) {
|
||||
let ENV = {
|
||||
modulePrefix: 'ghost-admin',
|
||||
environment,
|
||||
editorUrl: process.env.EDITOR_URL || '',
|
||||
rootURL: '',
|
||||
locationType: 'trailing-hash',
|
||||
EmberENV: {
|
||||
FEATURES: {
|
||||
// Here you can enable experimental features on an ember canary build
|
||||
// e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true
|
||||
},
|
||||
// @TODO verify that String/Function need to be enabled
|
||||
EXTEND_PROTOTYPES: {
|
||||
Date: false,
|
||||
Array: true,
|
||||
String: true,
|
||||
Function: false
|
||||
}
|
||||
},
|
||||
|
||||
APP: {
|
||||
// Here you can pass flags/options to your application instance
|
||||
// when it is created
|
||||
|
||||
// override the default version string which contains git info from
|
||||
// https://github.com/cibernox/git-repo-version. Only include the
|
||||
// `major.minor` version numbers
|
||||
version: require('../package.json').version.match(/^(\d+\.)?(\d+)/)[0]
|
||||
},
|
||||
|
||||
'ember-simple-auth': { },
|
||||
|
||||
'@sentry/ember': {
|
||||
disablePerformance: true,
|
||||
sentry: {}
|
||||
}
|
||||
};
|
||||
|
||||
if (environment === 'development') {
|
||||
// ENV.APP.LOG_RESOLVER = true;
|
||||
ENV.APP.LOG_ACTIVE_GENERATION = true;
|
||||
ENV.APP.LOG_TRANSITIONS = true;
|
||||
ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
|
||||
ENV.APP.LOG_VIEW_LOOKUPS = true;
|
||||
|
||||
// Enable mirage here in order to mock API endpoints during development
|
||||
ENV['ember-cli-mirage'] = {
|
||||
enabled: false
|
||||
};
|
||||
}
|
||||
|
||||
if (environment === 'test') {
|
||||
// Testem prefers this...
|
||||
ENV.rootURL = '/';
|
||||
ENV.locationType = 'none';
|
||||
|
||||
// keep test console output quieter
|
||||
ENV.APP.LOG_ACTIVE_GENERATION = false;
|
||||
ENV.APP.LOG_VIEW_LOOKUPS = false;
|
||||
|
||||
ENV.APP.rootElement = '#ember-testing';
|
||||
ENV.APP.autoboot = false;
|
||||
|
||||
// Without manually setting this, pretender won't track requests
|
||||
ENV['ember-cli-mirage'] = {
|
||||
trackRequests: true
|
||||
};
|
||||
|
||||
// We copy the dynamically loaded editor file into the ghost assets
|
||||
// directory in the dev/test env so that tests can load it. We need to
|
||||
// set the config appropriately here so that the fetchKoenigLexical
|
||||
// utility creates the right URL
|
||||
ENV.editorFilename = 'koenig-lexical.umd.js';
|
||||
ENV.editorHash = 'test';
|
||||
}
|
||||
|
||||
return ENV;
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"application-template-wrapper": false,
|
||||
"jquery-integration": true,
|
||||
"template-only-glimmer-components": true
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/* eslint-env node */
|
||||
|
||||
const browsers = [
|
||||
'last 2 Chrome versions',
|
||||
'last 2 Firefox versions',
|
||||
'last 3 Safari versions',
|
||||
'last 2 Edge versions'
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
browsers
|
||||
};
|
||||
@@ -0,0 +1,275 @@
|
||||
/* eslint-env node */
|
||||
'use strict';
|
||||
|
||||
// Check Node.js version compatibility before building
|
||||
require('./lib/check-node-version')();
|
||||
|
||||
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
|
||||
const concat = require('broccoli-concat');
|
||||
const mergeTrees = require('broccoli-merge-trees');
|
||||
const Terser = require('broccoli-terser-sourcemap');
|
||||
const Funnel = require('broccoli-funnel');
|
||||
const webpack = require('webpack');
|
||||
const environment = EmberApp.env();
|
||||
const isDevelopment = environment === 'development';
|
||||
const isProduction = environment === 'production';
|
||||
const isTesting = environment === 'test';
|
||||
|
||||
const postcssImport = require('postcss-import');
|
||||
const postcssCustomProperties = require('postcss-custom-properties');
|
||||
const postcssColorModFunction = require('postcss-color-mod-function');
|
||||
const postcssCustomMedia = require('postcss-custom-media');
|
||||
const autoprefixer = require('autoprefixer');
|
||||
const cssnano = require('cssnano');
|
||||
|
||||
const codemirrorAssets = function () {
|
||||
let codemirrorFiles = [
|
||||
'lib/codemirror.js',
|
||||
'mode/htmlmixed/htmlmixed.js',
|
||||
'mode/xml/xml.js',
|
||||
'mode/css/css.js',
|
||||
'mode/javascript/javascript.js'
|
||||
];
|
||||
|
||||
if (environment === 'test') {
|
||||
return {import: codemirrorFiles};
|
||||
}
|
||||
|
||||
let config = {};
|
||||
|
||||
config.public = {
|
||||
include: codemirrorFiles,
|
||||
destDir: '/',
|
||||
processTree(tree) {
|
||||
let jsTree = concat(tree, {
|
||||
outputFile: 'assets/codemirror/codemirror.js',
|
||||
headerFiles: ['lib/codemirror.js'],
|
||||
inputFiles: ['mode/**/*'],
|
||||
sourceMapConfig: {enabled: false}
|
||||
});
|
||||
|
||||
if (isProduction) {
|
||||
jsTree = new Terser(jsTree);
|
||||
}
|
||||
|
||||
let mergedTree = mergeTrees([tree, jsTree]);
|
||||
return new Funnel(mergedTree, {include: ['assets/**/*', 'theme/**/*']});
|
||||
}
|
||||
};
|
||||
|
||||
// put the files in vendor ready for importing into the test-support file
|
||||
if (environment === 'development') {
|
||||
config.vendor = codemirrorFiles;
|
||||
}
|
||||
|
||||
return config;
|
||||
};
|
||||
|
||||
let denylist = [];
|
||||
if (process.env.CI) {
|
||||
denylist.push('ember-cli-eslint');
|
||||
}
|
||||
|
||||
let publicAssetURL;
|
||||
|
||||
if (isTesting) {
|
||||
publicAssetURL = undefined;
|
||||
} else if (process.env.GHOST_CDN_URL) {
|
||||
publicAssetURL = process.env.GHOST_CDN_URL + 'assets/';
|
||||
} else {
|
||||
publicAssetURL = 'assets/';
|
||||
}
|
||||
|
||||
module.exports = function (defaults) {
|
||||
let app = new EmberApp(defaults, {
|
||||
addons: {denylist},
|
||||
babel: {
|
||||
plugins: [
|
||||
require.resolve('babel-plugin-transform-react-jsx')
|
||||
]
|
||||
},
|
||||
'ember-cli-babel': {
|
||||
optional: ['es6.spec.symbols'],
|
||||
includePolyfill: false
|
||||
},
|
||||
'ember-composable-helpers': {
|
||||
only: ['join', 'optional', 'pick', 'toggle', 'toggle-action', 'compute']
|
||||
},
|
||||
'ember-promise-modals': {
|
||||
excludeCSS: true
|
||||
},
|
||||
outputPaths: {
|
||||
app: {
|
||||
js: 'assets/ghost.js',
|
||||
css: {
|
||||
app: 'assets/ghost.css',
|
||||
// TODO: find a way to use the .min file with the lazyLoader
|
||||
'app-dark': 'assets/ghost-dark.css'
|
||||
}
|
||||
}
|
||||
},
|
||||
fingerprint: {
|
||||
enabled: isProduction,
|
||||
extensions: [
|
||||
'js',
|
||||
'css',
|
||||
'png',
|
||||
'jpg',
|
||||
'jpeg',
|
||||
'gif',
|
||||
'map',
|
||||
'svg',
|
||||
'ttf',
|
||||
'woff2',
|
||||
'mp4',
|
||||
'ico'
|
||||
],
|
||||
exclude: ['**/chunk*.map']
|
||||
},
|
||||
minifyJS: {
|
||||
options: {
|
||||
output: {
|
||||
semicolons: true
|
||||
}
|
||||
}
|
||||
},
|
||||
minifyCSS: {
|
||||
// postcss already handles minification and this was stripping required CSS
|
||||
enabled: false
|
||||
},
|
||||
nodeAssets: {
|
||||
codemirror: codemirrorAssets()
|
||||
},
|
||||
postcssOptions: {
|
||||
compile: {
|
||||
enabled: true,
|
||||
plugins: [
|
||||
{
|
||||
module: postcssImport,
|
||||
options: {
|
||||
path: ['app/styles']
|
||||
}
|
||||
},
|
||||
{
|
||||
module: postcssCustomProperties,
|
||||
options: {
|
||||
preserve: false
|
||||
}
|
||||
},
|
||||
{
|
||||
module: postcssColorModFunction
|
||||
},
|
||||
{
|
||||
module: postcssCustomMedia
|
||||
},
|
||||
{
|
||||
module: autoprefixer
|
||||
},
|
||||
{
|
||||
module: cssnano,
|
||||
options: {
|
||||
zindex: false,
|
||||
// cssnano sometimes minifies animations incorrectly causing them to break
|
||||
// See: https://github.com/ben-eb/gulp-cssnano/issues/33#issuecomment-210518957
|
||||
reduceIdents: {
|
||||
keyframes: false
|
||||
},
|
||||
discardUnused: {
|
||||
keyframes: false
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
sourcemaps: {enabled: true},
|
||||
svgJar: {
|
||||
strategy: 'inline',
|
||||
stripPath: false,
|
||||
sourceDirs: [
|
||||
'public/assets/icons'
|
||||
],
|
||||
optimizer: {
|
||||
plugins: [
|
||||
{prefixIds: true},
|
||||
{cleanupIds: false},
|
||||
{removeDimensions: true},
|
||||
{removeTitle: !isDevelopment},
|
||||
{removeXMLNS: true},
|
||||
// Transforms on groups are necessary to work around Firefox
|
||||
// not supporting transform-origin on line/path elements.
|
||||
{convertPathData: {
|
||||
applyTransforms: false
|
||||
}},
|
||||
{moveGroupAttrsToElems: false}
|
||||
]
|
||||
}
|
||||
},
|
||||
autoImport: {
|
||||
publicAssetURL,
|
||||
alias: {
|
||||
'sentry-testkit/browser': 'sentry-testkit/dist/browser'
|
||||
},
|
||||
webpack: {
|
||||
devtool: 'source-map',
|
||||
resolve: {
|
||||
fallback: {
|
||||
util: require.resolve('util'),
|
||||
path: require.resolve('path-browserify'),
|
||||
fs: false
|
||||
}
|
||||
},
|
||||
...(isDevelopment && {
|
||||
cache: {
|
||||
type: 'filesystem',
|
||||
buildDependencies: {
|
||||
config: [__filename]
|
||||
}
|
||||
}
|
||||
}),
|
||||
plugins: [
|
||||
new webpack.ProvidePlugin({
|
||||
process: 'process/browser'
|
||||
})
|
||||
],
|
||||
// disable verbose logging about webpack resolution mismatches
|
||||
// - this is a known issue with mismatched versions of dependencies resulting in duplication rather than a single hoisted version
|
||||
// - we don't plan on fixing this in the short term, so we just silence the noise
|
||||
infrastructureLogging: {
|
||||
level: 'error'
|
||||
}
|
||||
}
|
||||
},
|
||||
'ember-test-selectors': {
|
||||
strip: false
|
||||
}
|
||||
});
|
||||
|
||||
// Stop: Normalize
|
||||
app.import('node_modules/normalize.css/normalize.css');
|
||||
|
||||
// 'dem Styles
|
||||
// import codemirror styles rather than lazy-loading so that
|
||||
// our overrides work correctly
|
||||
app.import('node_modules/codemirror/lib/codemirror.css');
|
||||
app.import('node_modules/codemirror/theme/xq-light.css');
|
||||
|
||||
// 'dem Scripts
|
||||
app.import('node_modules/google-caja-bower/html-css-sanitizer-bundle.js');
|
||||
app.import('node_modules/keymaster/keymaster.js');
|
||||
app.import('node_modules/reframe.js/dist/noframe.js');
|
||||
|
||||
// pull things we rely on via lazy-loading into the test-support.js file so
|
||||
// that tests don't break when running via http://localhost:4200/tests
|
||||
if (app.env === 'development') {
|
||||
app.import('vendor/codemirror/lib/codemirror.js', {type: 'test'});
|
||||
}
|
||||
|
||||
if (app.env === 'development' || app.env === 'test') {
|
||||
// pull dynamic imports into the assets folder so that they can be lazy-loaded in tests
|
||||
// also done in development env so http://localhost:4200/tests works
|
||||
app.import('node_modules/@tryghost/koenig-lexical/dist/koenig-lexical.umd.js', {outputFile: 'ghost/assets/koenig-lexical/koenig-lexical.umd.js'});
|
||||
}
|
||||
|
||||
return app.toTree();
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"schemaVersion": "1.0.0",
|
||||
"packages": [
|
||||
{
|
||||
"name": "ember-cli",
|
||||
"version": "3.20.0",
|
||||
"blueprints": [
|
||||
{
|
||||
"name": "app",
|
||||
"outputRepo": "https://github.com/ember-cli/ember-new-output",
|
||||
"codemodsSource": "ember-app-codemods-manifest@1",
|
||||
"isBaseBlueprint": true,
|
||||
"options": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"compilerOptions":{"target":"es6","experimentalDecorators":true},"exclude":["node_modules","bower_components","tmp","vendor",".git","dist"]}
|
||||
@@ -0,0 +1,53 @@
|
||||
/* eslint-env node */
|
||||
'use strict';
|
||||
|
||||
const chalk = require('chalk');
|
||||
const semver = require('semver');
|
||||
|
||||
/**
|
||||
* Check Node.js version compatibility for Ember admin build
|
||||
*
|
||||
* The esm module (required by dependencies) has compatibility issues with
|
||||
* Node.js versions 22.10.0 to 22.17.x. We previously patched esm to work
|
||||
* around this, but to avoid maintaining patches, we now check the version
|
||||
* and provide clear guidance.
|
||||
*/
|
||||
function checkNodeVersion() {
|
||||
const nodeVersion = process.version;
|
||||
const parsedVersion = semver.parse(nodeVersion);
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
if (!parsedVersion) {
|
||||
console.warn(chalk.yellow(`Warning: Could not parse Node.js version: ${nodeVersion}`));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if version is in the problematic range: >=22.10.0 <22.18.0
|
||||
const isProblematicVersion = semver.satisfies(nodeVersion, '>=22.10.0 <22.18.0');
|
||||
|
||||
if (isProblematicVersion) {
|
||||
console.error('\n');
|
||||
console.error(chalk.red('='.repeat(80)));
|
||||
console.error(chalk.red('ERROR: Incompatible Node.js version detected'));
|
||||
console.error(chalk.red('='.repeat(80)));
|
||||
console.error();
|
||||
console.error(chalk.yellow(`Current Node.js version: ${chalk.bold(nodeVersion)}`));
|
||||
console.error();
|
||||
console.error(chalk.white('The Ember admin build requires the esm module, which has compatibility'));
|
||||
console.error(chalk.white('issues with Node.js versions 22.10.0 through 22.17.x.'));
|
||||
console.error();
|
||||
console.error(chalk.white('Please use one of the following Node.js versions:'));
|
||||
console.error(chalk.green(' • Node.js 22.18.0 or later'));
|
||||
console.error();
|
||||
console.error(chalk.white('To switch Node.js versions, you can use a version manager:'));
|
||||
console.error(chalk.cyan(' • nvm: nvm install 22.18.0 && nvm use 22.18.0'));
|
||||
console.error();
|
||||
console.error(chalk.red('='.repeat(80)));
|
||||
console.error();
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = checkNodeVersion;
|
||||
@@ -0,0 +1,6 @@
|
||||
/* eslint-env node */
|
||||
module.exports = {
|
||||
rules: {
|
||||
'brace-style': 'off'
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
/* eslint-disable ghost/ember/no-test-import-export */
|
||||
import {applyEmberDataSerializers, discoverEmberDataModels} from 'ember-cli-mirage';
|
||||
import {createServer} from 'miragejs';
|
||||
import {isTesting, macroCondition} from '@embroider/macros';
|
||||
|
||||
import devRoutes from './routes-dev';
|
||||
import testRoutes from './routes-test';
|
||||
|
||||
export default function (config) {
|
||||
let finalConfig = {
|
||||
...config,
|
||||
models: {...discoverEmberDataModels(), ...config.models},
|
||||
serializers: applyEmberDataSerializers(config.serializers),
|
||||
routes
|
||||
};
|
||||
|
||||
return createServer(finalConfig);
|
||||
}
|
||||
|
||||
function routes() {
|
||||
if (macroCondition(isTesting())) {
|
||||
testRoutes.call(this);
|
||||
} else {
|
||||
devRoutes.call(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import ghostPaths from 'ghost-admin/utils/ghost-paths';
|
||||
|
||||
export default function () {
|
||||
// allow any local requests outside of the namespace (configured below) to hit the real server
|
||||
// _must_ be called before the namespace property is set
|
||||
this.passthrough('/ghost/assets/**');
|
||||
|
||||
this.namespace = ghostPaths().apiRoot;
|
||||
this.timing = 1000; // delay for each request, automatically set to 0 during testing
|
||||
this.logging = true;
|
||||
|
||||
// Mock endpoints here to override real API requests during development, eg...
|
||||
// this.put('/posts/:id/', versionMismatchResponse);
|
||||
// mockTags(this);
|
||||
// this.loadFixtures('settings');
|
||||
|
||||
// keep this line, it allows all other API requests to hit the real server
|
||||
this.passthrough();
|
||||
|
||||
// add any external domains to make sure those get passed through too
|
||||
this.passthrough('http://www.gravatar.com/**');
|
||||
this.passthrough('https://cdn.jsdelivr.net/**');
|
||||
this.passthrough('https://api.unsplash.com/**');
|
||||
this.passthrough('https://ghost.org/**');
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
import ghostPaths from 'ghost-admin/utils/ghost-paths';
|
||||
|
||||
import mockApiKeys from './config/api-keys';
|
||||
import mockAuthentication from './config/authentication';
|
||||
import mockConfig from './config/config';
|
||||
import mockEmailPreview from './config/email-preview';
|
||||
import mockEmails from './config/emails';
|
||||
import mockIntegrations from './config/integrations';
|
||||
import mockInvites from './config/invites';
|
||||
import mockLabels from './config/labels';
|
||||
import mockMembers from './config/members';
|
||||
import mockNewsletters from './config/newsletters';
|
||||
import mockOffers from './config/offers';
|
||||
import mockPages from './config/pages';
|
||||
import mockPosts from './config/posts';
|
||||
import mockRoles from './config/roles';
|
||||
import mockSearchIndex from './config/search-index';
|
||||
import mockSettings from './config/settings';
|
||||
import mockSite from './config/site';
|
||||
import mockSlugs from './config/slugs';
|
||||
import mockSnippets from './config/snippets';
|
||||
import mockStats from './config/stats';
|
||||
import mockTags from './config/tags';
|
||||
import mockThemes from './config/themes';
|
||||
import mockTiers from './config/tiers';
|
||||
import mockUploads from './config/uploads';
|
||||
import mockUsers from './config/users';
|
||||
import mockWebhooks from './config/webhooks';
|
||||
|
||||
/* eslint-disable ghost/ember/no-test-import-export */
|
||||
export default function () {
|
||||
this.namespace = ghostPaths().apiRoot;
|
||||
// this.timing = 400; // delay for each request, automatically set to 0 during testing
|
||||
this.logging = false;
|
||||
|
||||
mockApiKeys(this);
|
||||
mockAuthentication(this);
|
||||
mockConfig(this);
|
||||
mockEmailPreview(this);
|
||||
mockEmails(this);
|
||||
mockIntegrations(this);
|
||||
mockInvites(this);
|
||||
mockMembers(this);
|
||||
mockLabels(this);
|
||||
mockPages(this);
|
||||
mockPosts(this);
|
||||
mockRoles(this);
|
||||
mockSearchIndex(this);
|
||||
mockSettings(this);
|
||||
mockSite(this);
|
||||
mockSlugs(this);
|
||||
mockTags(this);
|
||||
mockThemes(this);
|
||||
mockUploads(this);
|
||||
mockUsers(this);
|
||||
mockWebhooks(this);
|
||||
mockTiers(this);
|
||||
mockOffers(this);
|
||||
mockSnippets(this);
|
||||
mockNewsletters(this);
|
||||
mockStats(this);
|
||||
|
||||
/* Notifications -------------------------------------------------------- */
|
||||
|
||||
this.get('/notifications/');
|
||||
|
||||
/* Integrations - Slack Test Notification ------------------------------- */
|
||||
|
||||
this.post('/slack/test', function () {
|
||||
return {};
|
||||
});
|
||||
|
||||
/* Delete all ----------------------------------------------------------- */
|
||||
|
||||
this.del('/db', function (db) {
|
||||
db.posts.all().remove();
|
||||
db.tags.all().remove();
|
||||
}, 204);
|
||||
|
||||
/* limit=all blocker ---------------------------------------------------- */
|
||||
const originalHandledRequest = this.pretender.handledRequest;
|
||||
this.pretender.handledRequest = function (verb, path, request) {
|
||||
originalHandledRequest.call(this, verb, path, request);
|
||||
|
||||
const url = new URL(request.url, window.location.origin);
|
||||
const limit = url.searchParams.get('limit');
|
||||
|
||||
const ALLOWED_LIMIT_ALL = [
|
||||
'/api/admin/members/upload/'
|
||||
];
|
||||
|
||||
// limit=all is completely blocked, we shouldn't have any requests reach the server with this
|
||||
if (limit === 'all' && !ALLOWED_LIMIT_ALL.some(allowed => path.includes(allowed))) {
|
||||
throw new Error(`Blocked mirage request with limit=all: ${verb} ${path}.`);
|
||||
}
|
||||
|
||||
// limit > 100 is also blocked, apart from some specific endpoints
|
||||
if (limit && parseInt(limit, 10) > 100) {
|
||||
const parsedLimit = parseInt(limit, 10);
|
||||
|
||||
const SEARCH_ENDPOINTS_REGEX = /\/api\/admin\/(posts|pages|tags|users)\//;
|
||||
if (parsedLimit === 10000 && SEARCH_ENDPOINTS_REGEX.test(path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (path.match(/\/emails\/.*\/batches\//)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (path.includes('/api/admin/posts/export/')) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error(`Blocked mirage request with limit > 100: ${verb} ${path}.`);
|
||||
}
|
||||
};
|
||||
|
||||
/* External sites ------------------------------------------------------- */
|
||||
|
||||
this.head('http://www.gravatar.com/avatar/:md5', function () {
|
||||
return '';
|
||||
}, 200);
|
||||
|
||||
this.get('http://www.gravatar.com/avatar/:md5', function () {
|
||||
return '';
|
||||
}, 200);
|
||||
|
||||
this.get('https://ghost.org/changelog.json', function () {
|
||||
return {
|
||||
posts: [
|
||||
{
|
||||
title: 'Custom image alt tags',
|
||||
custom_excerpt: 'Alt tag support for images in the Ghost editor',
|
||||
slug: 'image-alt-text-support',
|
||||
published_at: '2019-08-05T07:46:16.000+00:00',
|
||||
url: 'https://ghost.org/changelog/image-alt-text-support/',
|
||||
featured: 'false'
|
||||
}
|
||||
],
|
||||
changelogUrl: 'https://ghost.org/changelog/'
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
import {Response} from 'miragejs';
|
||||
import {isArray} from '@ember/array';
|
||||
|
||||
export function paginatedResponse(modelName) {
|
||||
return function (schema, request) {
|
||||
let page = +request.queryParams.page || 1;
|
||||
let limit = request.queryParams.limit;
|
||||
let collection = schema[modelName].all();
|
||||
|
||||
if (limit !== 'all') {
|
||||
limit = +request.queryParams.limit || 15;
|
||||
}
|
||||
|
||||
return paginateModelCollection(modelName, collection, page, limit);
|
||||
};
|
||||
}
|
||||
|
||||
export function paginateModelCollection(modelName, collection, page, limit) {
|
||||
let pages, next, prev, models;
|
||||
|
||||
page = parseInt(page, 10);
|
||||
limit = parseInt(limit, 10);
|
||||
|
||||
if (limit === 'all') {
|
||||
pages = 1;
|
||||
} else {
|
||||
limit = +limit;
|
||||
|
||||
let start = (page - 1) * limit;
|
||||
let end = start + limit;
|
||||
|
||||
pages = Math.ceil(collection.models.length / limit);
|
||||
models = collection.models.slice(start, end);
|
||||
|
||||
if (start > 0) {
|
||||
prev = page - 1;
|
||||
}
|
||||
|
||||
if (end < collection.models.length) {
|
||||
next = page + 1;
|
||||
}
|
||||
}
|
||||
|
||||
collection.meta = {
|
||||
pagination: {
|
||||
page,
|
||||
limit,
|
||||
pages,
|
||||
total: collection.models.length,
|
||||
next: next || null,
|
||||
prev: prev || null
|
||||
}
|
||||
};
|
||||
|
||||
if (models) {
|
||||
collection.models = models;
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
export function maintenanceResponse() {
|
||||
return new Response(503, {}, {
|
||||
errors: [{
|
||||
type: 'Maintenance'
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
export function versionMismatchResponse() {
|
||||
return new Response(400, {}, {
|
||||
errors: [{
|
||||
type: 'VersionMismatchError'
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeBooleanParams(arr) {
|
||||
if (!isArray(arr)) {
|
||||
return arr;
|
||||
}
|
||||
|
||||
return arr.map((i) => {
|
||||
if (i === 'true') {
|
||||
return true;
|
||||
} else if (i === 'false') {
|
||||
return false;
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeStringParams(arr) {
|
||||
if (!isArray(arr)) {
|
||||
return arr;
|
||||
}
|
||||
|
||||
return arr.map((i) => {
|
||||
if (!i.replace) {
|
||||
return i;
|
||||
}
|
||||
|
||||
return i.replace(/^['"]|['"]$/g, '');
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: use GQL to parse filter string?
|
||||
export function extractFilterParam(param, filter = '') {
|
||||
let filterRegex = new RegExp(`${param}:(.*?)(?:\\+|$)`);
|
||||
let match;
|
||||
|
||||
let [, result] = filter.match(filterRegex) || [];
|
||||
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.startsWith('[')) {
|
||||
match = result.replace(/^\[|\]$/g, '').split(',');
|
||||
} else if (result.startsWith('~')) {
|
||||
match = result.replace(/^~/, '').replace(/\\'/g, `'`).replace(/^'|'$/g, '');
|
||||
} else {
|
||||
match = [result];
|
||||
}
|
||||
|
||||
return normalizeBooleanParams(normalizeStringParams(match));
|
||||
}
|
||||
|
||||
export function hasInvalidPermissions(allowedRoles) {
|
||||
const {schema, request} = this;
|
||||
|
||||
// always allow dev requests through - the logged in user will be real so
|
||||
// we can't check against it in the mocked db
|
||||
if (!request.requestHeaders['X-Test-User']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const invalidPermsResponse = new Response(403, {}, {
|
||||
errors: [{
|
||||
type: 'NoPermissionError',
|
||||
message: 'You do not have permission to perform this action'
|
||||
}]
|
||||
});
|
||||
|
||||
const user = schema.users.find(request.requestHeaders['X-Test-User']);
|
||||
const adminRoles = user.roles.filter(role => allowedRoles.includes(role.name));
|
||||
|
||||
if (adminRoles.length === 0) {
|
||||
return invalidPermsResponse;
|
||||
}
|
||||
}
|
||||
|
||||
export function withPermissionsCheck(allowedRoles, fn) {
|
||||
return function () {
|
||||
const boundPermsCheck = hasInvalidPermissions.bind(this);
|
||||
const boundFn = fn.bind(this);
|
||||
return boundPermsCheck(allowedRoles) || boundFn(...arguments);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
{
|
||||
"name": "ghost-admin",
|
||||
"version": "6.33.0-rc.0",
|
||||
"description": "Ember.js admin client for Ghost",
|
||||
"author": "Ghost Foundation",
|
||||
"homepage": "http://ghost.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/TryGhost/Admin.git"
|
||||
},
|
||||
"bugs": "https://github.com/TryGhost/Ghost/issues",
|
||||
"contributors": "https://github.com/TryGhost/Admin/graphs/contributors",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"directories": {
|
||||
"test": "tests"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "ember serve",
|
||||
"build": "ember build --environment=production --silent",
|
||||
"build:dev": "pnpm build --environment=development",
|
||||
"test:unit": "true",
|
||||
"test": "ember exam --split 2 --parallel",
|
||||
"lint:js": "eslint . --cache",
|
||||
"lint:hbs": "ember-template-lint .",
|
||||
"lint": "pnpm lint:js && pnpm lint:hbs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^22.13.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "7.28.4",
|
||||
"@babel/plugin-proposal-class-properties": "7.18.6",
|
||||
"@babel/plugin-proposal-decorators": "7.28.0",
|
||||
"@ember/jquery": "2.0.0",
|
||||
"@ember/optional-features": "2.1.0",
|
||||
"@ember/render-modifiers": "2.1.0",
|
||||
"@ember/test-helpers": "2.9.6",
|
||||
"@ember/test-waiters": "3.1.0",
|
||||
"@embroider/macros": "1.16.13",
|
||||
"@faker-js/faker": "7.6.0",
|
||||
"@glimmer/component": "1.1.2",
|
||||
"@html-next/vertical-collection": "3.0.0",
|
||||
"@sentry/ember": "7.120.3",
|
||||
"@sentry/integrations": "7.114.0",
|
||||
"@sentry/replay": "7.116.0",
|
||||
"@tryghost/admin-x-framework": "workspace:*",
|
||||
"ghost": "workspace:*",
|
||||
"@tryghost/color-utils": "0.2.16",
|
||||
"@tryghost/ember-promise-modals": "2.0.1",
|
||||
"@tryghost/helpers": "1.1.103",
|
||||
"@tryghost/kg-clean-basic-html": "4.2.23",
|
||||
"@tryghost/kg-converters": "1.1.21",
|
||||
"@tryghost/koenig-lexical": "1.7.30",
|
||||
"@tryghost/limit-service": "1.5.2",
|
||||
"@tryghost/members-csv": "2.0.5",
|
||||
"@tryghost/nql": "0.12.10",
|
||||
"@tryghost/nql-lang": "0.6.4",
|
||||
"@tryghost/string": "0.3.2",
|
||||
"@tryghost/timezone-data": "0.4.18",
|
||||
"animejs": "3.2.2",
|
||||
"autoprefixer": "9.8.6",
|
||||
"babel-plugin-transform-class-properties": "6.24.1",
|
||||
"babel-plugin-transform-react-jsx": "6.24.1",
|
||||
"broccoli-asset-rev": "3.0.0",
|
||||
"broccoli-concat": "4.2.7",
|
||||
"broccoli-funnel": "3.0.8",
|
||||
"broccoli-merge-trees": "4.2.0",
|
||||
"broccoli-terser-sourcemap": "4.1.1",
|
||||
"chai": "4.5.0",
|
||||
"chai-dom": "1.12.1",
|
||||
"codemirror": "5.48.2",
|
||||
"cssnano": "4.1.10",
|
||||
"element-resize-detector": "1.2.4",
|
||||
"ember-ajax": "5.1.2",
|
||||
"ember-assign-helper": "0.5.0",
|
||||
"ember-auto-import": "2.10.0",
|
||||
"ember-classic-decorator": "3.0.1",
|
||||
"ember-cli": "3.24.0",
|
||||
"ember-cli-app-version": "5.0.0",
|
||||
"ember-cli-babel": "8.2.0",
|
||||
"ember-cli-chart": "3.7.2",
|
||||
"ember-cli-code-coverage": "1.0.3",
|
||||
"ember-cli-dependency-checker": "3.3.2",
|
||||
"ember-cli-deprecation-workflow": "2.2.0",
|
||||
"ember-cli-htmlbars": "6.3.0",
|
||||
"ember-cli-inject-live-reload": "2.1.0",
|
||||
"ember-cli-mirage": "2.4.0",
|
||||
"ember-cli-node-assets": "0.2.2",
|
||||
"ember-cli-postcss": "6.0.1",
|
||||
"ember-cli-shims": "1.2.0",
|
||||
"ember-cli-string-helpers": "6.1.0",
|
||||
"ember-cli-terser": "4.0.1",
|
||||
"ember-cli-test-loader": "3.1.0",
|
||||
"ember-composable-helpers": "5.0.0",
|
||||
"ember-concurrency": "2.3.7",
|
||||
"ember-could-get-used-to-this": "1.0.1",
|
||||
"ember-css-transitions": "4.4.1",
|
||||
"ember-data": "3.24.0",
|
||||
"ember-decorators": "6.1.1",
|
||||
"ember-drag-drop": "0.4.8",
|
||||
"ember-ella-sparse": "0.16.0",
|
||||
"ember-exam": "6.0.1",
|
||||
"ember-export-application-global": "2.0.1",
|
||||
"ember-fetch": "8.1.2",
|
||||
"ember-in-viewport": "4.1.0",
|
||||
"ember-infinity": "2.3.0",
|
||||
"ember-keyboard": "8.2.1",
|
||||
"ember-load": "0.0.17",
|
||||
"ember-load-initializers": "2.1.2",
|
||||
"ember-mocha": "0.16.2",
|
||||
"ember-modifier": "4.2.0",
|
||||
"ember-moment": "10.0.1",
|
||||
"ember-one-way-select": "4.0.1",
|
||||
"ember-power-datepicker": "0.8.1",
|
||||
"ember-power-select": "6.0.1",
|
||||
"ember-resolver": "8.1.0",
|
||||
"ember-simple-auth": "5.0.0",
|
||||
"ember-sinon": "5.0.0",
|
||||
"ember-source": "3.24.0",
|
||||
"ember-svg-jar": "2.7.1",
|
||||
"ember-template-lint": "5.13.0",
|
||||
"ember-test-selectors": "6.0.0",
|
||||
"ember-tooltips": "3.6.0",
|
||||
"ember-truth-helpers": "3.1.1",
|
||||
"eslint": "catalog:",
|
||||
"eslint-plugin-babel": "5.3.1",
|
||||
"flexsearch": "0.7.43",
|
||||
"fs-extra": "11.3.4",
|
||||
"glob": "8.1.0",
|
||||
"google-caja-bower": "https://github.com/acburdine/google-caja-bower#ghost",
|
||||
"keymaster": "https://github.com/madrobby/keymaster.git",
|
||||
"liquid-fire": "0.34.0",
|
||||
"liquid-wormhole": "3.0.1",
|
||||
"loader.js": "4.7.0",
|
||||
"microdiff": "1.5.0",
|
||||
"miragejs": "0.1.48",
|
||||
"moment-timezone": "0.5.45",
|
||||
"normalize.css": "3.0.3",
|
||||
"papaparse": "5.5.3",
|
||||
"postcss-color-mod-function": "3.0.3",
|
||||
"postcss-custom-media": "7.0.8",
|
||||
"postcss-custom-properties": "10.0.0",
|
||||
"postcss-import": "12.0.1",
|
||||
"pretender": "3.4.7",
|
||||
"process": "0.11.10",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"reframe.js": "4.0.2",
|
||||
"semver": "7.7.4",
|
||||
"sentry-testkit": "5.0.10",
|
||||
"sinon-chai": "4.0.1",
|
||||
"testem": "3.19.1",
|
||||
"tracked-built-ins": "3.4.0",
|
||||
"util": "0.12.5",
|
||||
"validator": "13.12.0",
|
||||
"walk-sync": "3.0.0"
|
||||
},
|
||||
"ember-addon": {
|
||||
"paths": [
|
||||
"lib/asset-delivery",
|
||||
"lib/ember-power-calendar-moment",
|
||||
"lib/ember-power-calendar-utils"
|
||||
]
|
||||
},
|
||||
"ember": {
|
||||
"edition": "octane"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.hbs": "ember-template-lint",
|
||||
"*.js": "eslint"
|
||||
},
|
||||
"dependencies": {
|
||||
"i18n-iso-countries": "7.14.0",
|
||||
"lru-cache": "6.0.0",
|
||||
"path-browserify": "1.0.1",
|
||||
"webpack": "5.105.4"
|
||||
},
|
||||
"nx": {
|
||||
"implicitDependencies": [
|
||||
"!ghost"
|
||||
],
|
||||
"targets": {
|
||||
"build:dev": {
|
||||
"dependsOn": [
|
||||
"build:dev",
|
||||
{
|
||||
"projects": [
|
||||
"@tryghost/admin-x-framework",
|
||||
"@tryghost/admin-x-settings",
|
||||
"@tryghost/activitypub",
|
||||
"@tryghost/posts",
|
||||
"@tryghost/stats"
|
||||
],
|
||||
"target": "build"
|
||||
}
|
||||
]
|
||||
},
|
||||
"build": {
|
||||
"outputs": [
|
||||
"{projectRoot}/dist",
|
||||
"{workspaceRoot}/ghost/core/core/built/admin"
|
||||
],
|
||||
"dependsOn": [
|
||||
"build",
|
||||
{
|
||||
"projects": [
|
||||
"@tryghost/admin-x-framework",
|
||||
"@tryghost/admin-x-settings",
|
||||
"@tryghost/activitypub",
|
||||
"@tryghost/posts",
|
||||
"@tryghost/stats"
|
||||
],
|
||||
"target": "build"
|
||||
}
|
||||
]
|
||||
},
|
||||
"test": {
|
||||
"dependsOn": [
|
||||
{
|
||||
"projects": [
|
||||
"@tryghost/admin-x-framework"
|
||||
],
|
||||
"target": "build"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/* eslint-env node */
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
let launch_in_ci = [process.env.BROWSER || 'Chrome'];
|
||||
|
||||
module.exports = {
|
||||
framework: 'mocha',
|
||||
browser_start_timeout: 120,
|
||||
browser_disconnect_timeout: 60,
|
||||
test_page: 'tests/index.html?hidepassed',
|
||||
disable_watching: true,
|
||||
parallel: process.env.EMBER_EXAM_SPLIT_COUNT || 1,
|
||||
launch_in_ci,
|
||||
launch_in_dev: [
|
||||
'Chrome',
|
||||
'Firefox'
|
||||
],
|
||||
browser_args: {
|
||||
Chrome: {
|
||||
ci: [
|
||||
// --no-sandbox is needed when running Chrome inside a container
|
||||
process.env.CI ? '--no-sandbox' : null,
|
||||
'--headless',
|
||||
'--disable-dev-shm-usage',
|
||||
'--disable-software-rasterizer',
|
||||
'--mute-audio',
|
||||
'--remote-debugging-port=0',
|
||||
'--window-size=1440,900'
|
||||
].filter(Boolean)
|
||||
},
|
||||
Firefox: {
|
||||
ci: ['-headless']
|
||||
}
|
||||
},
|
||||
tap_failed_tests_only: true
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<base href="{{rootURL}}">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Ghost Tests</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
{{content-for "head"}}
|
||||
{{content-for "test-head"}}
|
||||
|
||||
<link rel="stylesheet" href="{{rootURL}}assets/vendor.css">
|
||||
<link rel="stylesheet" href="{{rootURL}}assets/ghost.css">
|
||||
<link rel="stylesheet" href="{{rootURL}}assets/test-support.css">
|
||||
|
||||
{{content-for "head-footer"}}
|
||||
{{content-for "test-head-footer"}}
|
||||
|
||||
<style>
|
||||
/* fix to ensure we use full viewport height when testing */
|
||||
#ember-testing {
|
||||
height: 100%;
|
||||
}
|
||||
#ember-testing .gh-app {
|
||||
position: relative;
|
||||
}
|
||||
/* fix firefox not supporting `zoom: 50%` */
|
||||
_::-moz-range-track, body:last-child #ember-testing {
|
||||
-moz-transform-origin: 0 0;
|
||||
-moz-transform: scale(0.5);
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{{content-for "body"}}
|
||||
{{content-for "test-body"}}
|
||||
|
||||
<script src="/testem.js" integrity=""></script>
|
||||
<script src="{{rootURL}}assets/vendor.js"></script>
|
||||
<script src="{{rootURL}}assets/test-support.js"></script>
|
||||
<script src="{{rootURL}}assets/ghost.js"></script>
|
||||
<script src="{{rootURL}}assets/tests.js"></script>
|
||||
|
||||
{{content-for "body-footer"}}
|
||||
{{content-for "test-body-footer"}}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,22 @@
|
||||
import Application from 'ghost-admin/app';
|
||||
import config from 'ghost-admin/config/environment';
|
||||
import registerWaiter from 'ember-raf-scheduler/test-support/register-waiter';
|
||||
import start from 'ember-exam/test-support/start';
|
||||
import {setApplication} from '@ember/test-helpers';
|
||||
|
||||
import chai from 'chai';
|
||||
import chaiDom from 'chai-dom';
|
||||
import sinonChai from 'sinon-chai';
|
||||
chai.use(chaiDom);
|
||||
chai.use(sinonChai);
|
||||
|
||||
setApplication(Application.create(config.APP));
|
||||
|
||||
registerWaiter();
|
||||
|
||||
mocha.setup({
|
||||
timeout: 15000,
|
||||
slow: 500
|
||||
});
|
||||
|
||||
start();
|
||||
Reference in New Issue
Block a user