ghost 5.111.0 → 5.113.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.
Files changed (167) hide show
  1. package/components/{tryghost-adapter-cache-redis-5.111.0.tgz → tryghost-adapter-cache-redis-5.113.0.tgz} +0 -0
  2. package/components/{tryghost-adapter-manager-5.111.0.tgz → tryghost-adapter-manager-5.113.0.tgz} +0 -0
  3. package/components/tryghost-announcement-bar-settings-5.113.0.tgz +0 -0
  4. package/components/{tryghost-api-framework-5.111.0.tgz → tryghost-api-framework-5.113.0.tgz} +0 -0
  5. package/components/{tryghost-api-version-compatibility-service-5.111.0.tgz → tryghost-api-version-compatibility-service-5.113.0.tgz} +0 -0
  6. package/components/{tryghost-audience-feedback-5.111.0.tgz → tryghost-audience-feedback-5.113.0.tgz} +0 -0
  7. package/components/tryghost-bookshelf-repository-5.113.0.tgz +0 -0
  8. package/components/{tryghost-bootstrap-socket-5.111.0.tgz → tryghost-bootstrap-socket-5.113.0.tgz} +0 -0
  9. package/components/tryghost-captcha-service-5.113.0.tgz +0 -0
  10. package/components/tryghost-constants-5.113.0.tgz +0 -0
  11. package/components/tryghost-custom-fonts-5.113.0.tgz +0 -0
  12. package/components/tryghost-custom-theme-settings-service-5.113.0.tgz +0 -0
  13. package/components/{tryghost-data-generator-5.111.0.tgz → tryghost-data-generator-5.113.0.tgz} +0 -0
  14. package/components/tryghost-domain-events-5.113.0.tgz +0 -0
  15. package/components/tryghost-donations-5.113.0.tgz +0 -0
  16. package/components/tryghost-email-addresses-5.113.0.tgz +0 -0
  17. package/components/{tryghost-email-analytics-provider-mailgun-5.111.0.tgz → tryghost-email-analytics-provider-mailgun-5.113.0.tgz} +0 -0
  18. package/components/tryghost-email-analytics-service-5.113.0.tgz +0 -0
  19. package/components/{tryghost-email-content-generator-5.111.0.tgz → tryghost-email-content-generator-5.113.0.tgz} +0 -0
  20. package/components/tryghost-email-events-5.113.0.tgz +0 -0
  21. package/components/tryghost-email-service-5.113.0.tgz +0 -0
  22. package/components/{tryghost-email-suppression-list-5.111.0.tgz → tryghost-email-suppression-list-5.113.0.tgz} +0 -0
  23. package/components/{tryghost-express-dynamic-redirects-5.111.0.tgz → tryghost-express-dynamic-redirects-5.113.0.tgz} +0 -0
  24. package/components/tryghost-extract-api-key-5.113.0.tgz +0 -0
  25. package/components/tryghost-ghost-5.113.0.tgz +0 -0
  26. package/components/{tryghost-html-to-plaintext-5.111.0.tgz → tryghost-html-to-plaintext-5.113.0.tgz} +0 -0
  27. package/components/tryghost-i18n-5.113.0.tgz +0 -0
  28. package/components/{tryghost-identity-token-service-5.111.0.tgz → tryghost-identity-token-service-5.113.0.tgz} +0 -0
  29. package/components/{tryghost-importer-handler-content-files-5.111.0.tgz → tryghost-importer-handler-content-files-5.113.0.tgz} +0 -0
  30. package/components/{tryghost-importer-revue-5.111.0.tgz → tryghost-importer-revue-5.113.0.tgz} +0 -0
  31. package/components/tryghost-in-memory-repository-5.113.0.tgz +0 -0
  32. package/components/tryghost-job-manager-5.113.0.tgz +0 -0
  33. package/components/tryghost-link-redirects-5.113.0.tgz +0 -0
  34. package/components/tryghost-link-replacer-5.113.0.tgz +0 -0
  35. package/components/{tryghost-magic-link-5.111.0.tgz → tryghost-magic-link-5.113.0.tgz} +0 -0
  36. package/components/tryghost-mail-events-5.113.0.tgz +0 -0
  37. package/components/tryghost-mailgun-client-5.113.0.tgz +0 -0
  38. package/components/tryghost-member-attribution-5.113.0.tgz +0 -0
  39. package/components/tryghost-member-events-5.113.0.tgz +0 -0
  40. package/components/{tryghost-members-api-5.111.0.tgz → tryghost-members-api-5.113.0.tgz} +0 -0
  41. package/components/{tryghost-members-csv-5.111.0.tgz → tryghost-members-csv-5.113.0.tgz} +0 -0
  42. package/components/{tryghost-members-importer-5.111.0.tgz → tryghost-members-importer-5.113.0.tgz} +0 -0
  43. package/components/{tryghost-members-offers-5.111.0.tgz → tryghost-members-offers-5.113.0.tgz} +0 -0
  44. package/components/tryghost-members-payments-5.113.0.tgz +0 -0
  45. package/components/{tryghost-members-ssr-5.111.0.tgz → tryghost-members-ssr-5.113.0.tgz} +0 -0
  46. package/components/{tryghost-members-stripe-service-5.111.0.tgz → tryghost-members-stripe-service-5.113.0.tgz} +0 -0
  47. package/components/tryghost-milestones-5.113.0.tgz +0 -0
  48. package/components/{tryghost-minifier-5.111.0.tgz → tryghost-minifier-5.113.0.tgz} +0 -0
  49. package/components/{tryghost-mw-api-version-mismatch-5.111.0.tgz → tryghost-mw-api-version-mismatch-5.113.0.tgz} +0 -0
  50. package/components/tryghost-mw-cache-control-5.113.0.tgz +0 -0
  51. package/components/{tryghost-mw-error-handler-5.111.0.tgz → tryghost-mw-error-handler-5.113.0.tgz} +0 -0
  52. package/components/{tryghost-mw-session-from-token-5.111.0.tgz → tryghost-mw-session-from-token-5.113.0.tgz} +0 -0
  53. package/components/{tryghost-mw-update-user-last-seen-5.111.0.tgz → tryghost-mw-update-user-last-seen-5.113.0.tgz} +0 -0
  54. package/components/{tryghost-mw-version-match-5.111.0.tgz → tryghost-mw-version-match-5.113.0.tgz} +0 -0
  55. package/components/tryghost-mw-vhost-5.113.0.tgz +0 -0
  56. package/components/{tryghost-package-json-5.111.0.tgz → tryghost-package-json-5.113.0.tgz} +0 -0
  57. package/components/tryghost-post-events-5.113.0.tgz +0 -0
  58. package/components/tryghost-post-revisions-5.113.0.tgz +0 -0
  59. package/components/{tryghost-posts-service-5.111.0.tgz → tryghost-posts-service-5.113.0.tgz} +0 -0
  60. package/components/{tryghost-prometheus-metrics-5.111.0.tgz → tryghost-prometheus-metrics-5.113.0.tgz} +0 -0
  61. package/components/tryghost-recommendations-5.113.0.tgz +0 -0
  62. package/components/tryghost-referrers-5.113.0.tgz +0 -0
  63. package/components/{tryghost-security-5.111.0.tgz → tryghost-security-5.113.0.tgz} +0 -0
  64. package/components/tryghost-session-service-5.113.0.tgz +0 -0
  65. package/components/{tryghost-settings-path-manager-5.111.0.tgz → tryghost-settings-path-manager-5.113.0.tgz} +0 -0
  66. package/components/{tryghost-slack-notifications-5.111.0.tgz → tryghost-slack-notifications-5.113.0.tgz} +0 -0
  67. package/components/{tryghost-tiers-5.111.0.tgz → tryghost-tiers-5.113.0.tgz} +0 -0
  68. package/components/tryghost-version-notifications-data-service-5.113.0.tgz +0 -0
  69. package/components/{tryghost-webmentions-5.111.0.tgz → tryghost-webmentions-5.113.0.tgz} +0 -0
  70. package/core/boot.js +0 -3
  71. package/core/built/admin/assets/admin-x-activitypub/admin-x-activitypub.js +13043 -11763
  72. package/core/built/admin/assets/admin-x-settings/{CodeEditorView-1298238e.mjs → CodeEditorView-ed5e87be.mjs} +2 -2
  73. package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +1 -1
  74. package/core/built/admin/assets/admin-x-settings/{index-0f51ccb5.mjs → index-0ee4d13c.mjs} +2 -2
  75. package/core/built/admin/assets/admin-x-settings/{index-2707471f.mjs → index-9c7da716.mjs} +20224 -20178
  76. package/core/built/admin/assets/admin-x-settings/{modals-f5983704.mjs → modals-7708d510.mjs} +2227 -2216
  77. package/core/built/admin/assets/{chunk.524.405c43b2cb20553b51d9.js → chunk.524.4f0aeb6b611079e528f5.js} +7 -7
  78. package/core/built/admin/assets/{chunk.582.eb4b096f29c97c9d6a64.js → chunk.582.485df00698ed27a0668b.js} +8 -8
  79. package/core/built/admin/assets/{ghost-87cffc153ec73d217c1ae9f9207ea5e1.js → ghost-ebf07ae7768b6e9fb9a4b173b6917782.js} +72 -70
  80. package/core/built/admin/assets/img/ap-nodes-01ee317529e6353a1c34a062c388f1e7.png +0 -0
  81. package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +12944 -12924
  82. package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +136 -134
  83. package/core/built/admin/assets/posts/posts.js +2 -2
  84. package/core/built/admin/assets/{vendor-fca15534b8426c0567400113c63a3e21.js → vendor-68a4aa424a179a90f5bbc2b750def576.js} +28 -26
  85. package/core/built/admin/index.html +4 -4
  86. package/core/frontend/helpers/get.js +2 -3
  87. package/core/frontend/services/routing/registry.js +6 -6
  88. package/core/frontend/src/admin-auth/message-handler.js +1 -1
  89. package/core/frontend/src/cards/css/cta.css +38 -36
  90. package/core/server/adapters/cache/AdapterCacheMemoryTTL.js +54 -0
  91. package/core/server/adapters/cache/memory-ttl.js +1 -1
  92. package/core/server/api/endpoints/utils/serializers/input/settings.js +2 -1
  93. package/core/server/api/endpoints/utils/serializers/input/utils/settings-key-group-mapper.js +2 -1
  94. package/core/server/api/endpoints/utils/serializers/input/utils/settings-key-type-mapper.js +2 -1
  95. package/core/server/data/migrations/versions/5.112/2025-03-10-10-01-01-add-require-mfa-setting.js +8 -0
  96. package/core/server/data/migrations/versions/5.113/2025-03-07-12-24-00-add-super-editor.js +31 -0
  97. package/core/server/data/migrations/versions/5.113/2025-03-07-12-25-00-add-member-perms-to-super-editor.js +291 -0
  98. package/core/server/data/schema/default-settings/default-settings.json +6 -0
  99. package/core/server/data/schema/fixtures/fixtures.json +27 -0
  100. package/core/server/models/invite.js +6 -7
  101. package/core/server/models/post.js +3 -9
  102. package/core/server/models/relations/authors.js +2 -4
  103. package/core/server/models/role-utils.js +38 -0
  104. package/core/server/models/role.js +7 -5
  105. package/core/server/models/user.js +41 -28
  106. package/core/server/services/activitypub/ActivityPubService.js +116 -0
  107. package/core/server/services/activitypub/ActivityPubService.ts +139 -0
  108. package/core/server/services/activitypub/ActivityPubServiceWrapper.js +1 -1
  109. package/core/server/services/email-analytics/jobs/update-member-email-analytics/index.js +13 -0
  110. package/core/server/services/email-analytics/lib/queries.js +3 -3
  111. package/core/server/services/media-inliner/ExternalMediaInliner.js +346 -0
  112. package/core/server/services/media-inliner/service.js +1 -1
  113. package/core/server/services/permissions/can-this.js +3 -2
  114. package/core/server/services/stats/MembersStatsService.js +167 -0
  115. package/core/server/services/stats/MrrStatsService.js +161 -0
  116. package/core/server/services/stats/ReferrersStatsService.js +164 -0
  117. package/core/server/services/stats/StatsService.js +63 -0
  118. package/core/server/services/stats/SubscriptionStatsService.js +180 -0
  119. package/core/server/services/stats/service.js +1 -1
  120. package/core/server/services/url/Resources.js +20 -30
  121. package/core/server/services/url/UrlService.js +2 -12
  122. package/core/server/services/url/Urls.js +17 -33
  123. package/core/shared/config/defaults.json +1 -1
  124. package/core/shared/labs.js +2 -1
  125. package/core/shared/settings-cache/CacheManager.js +4 -4
  126. package/package.json +139 -142
  127. package/tsconfig.tsbuildinfo +1 -1
  128. package/yarn.lock +40 -82
  129. package/components/tryghost-activitypub-5.111.0.tgz +0 -0
  130. package/components/tryghost-adapter-cache-memory-ttl-5.111.0.tgz +0 -0
  131. package/components/tryghost-announcement-bar-settings-5.111.0.tgz +0 -0
  132. package/components/tryghost-bookshelf-repository-5.111.0.tgz +0 -0
  133. package/components/tryghost-captcha-service-5.111.0.tgz +0 -0
  134. package/components/tryghost-constants-5.111.0.tgz +0 -0
  135. package/components/tryghost-custom-fonts-5.111.0.tgz +0 -0
  136. package/components/tryghost-custom-theme-settings-service-5.111.0.tgz +0 -0
  137. package/components/tryghost-domain-events-5.111.0.tgz +0 -0
  138. package/components/tryghost-donations-5.111.0.tgz +0 -0
  139. package/components/tryghost-email-addresses-5.111.0.tgz +0 -0
  140. package/components/tryghost-email-analytics-service-5.111.0.tgz +0 -0
  141. package/components/tryghost-email-events-5.111.0.tgz +0 -0
  142. package/components/tryghost-email-service-5.111.0.tgz +0 -0
  143. package/components/tryghost-external-media-inliner-5.111.0.tgz +0 -0
  144. package/components/tryghost-extract-api-key-5.111.0.tgz +0 -0
  145. package/components/tryghost-ghost-5.111.0.tgz +0 -0
  146. package/components/tryghost-i18n-5.111.0.tgz +0 -0
  147. package/components/tryghost-in-memory-repository-5.111.0.tgz +0 -0
  148. package/components/tryghost-job-manager-5.111.0.tgz +0 -0
  149. package/components/tryghost-link-redirects-5.111.0.tgz +0 -0
  150. package/components/tryghost-link-replacer-5.111.0.tgz +0 -0
  151. package/components/tryghost-mail-events-5.111.0.tgz +0 -0
  152. package/components/tryghost-mailgun-client-5.111.0.tgz +0 -0
  153. package/components/tryghost-member-attribution-5.111.0.tgz +0 -0
  154. package/components/tryghost-member-events-5.111.0.tgz +0 -0
  155. package/components/tryghost-members-payments-5.111.0.tgz +0 -0
  156. package/components/tryghost-milestones-5.111.0.tgz +0 -0
  157. package/components/tryghost-mw-cache-control-5.111.0.tgz +0 -0
  158. package/components/tryghost-mw-vhost-5.111.0.tgz +0 -0
  159. package/components/tryghost-post-events-5.111.0.tgz +0 -0
  160. package/components/tryghost-post-revisions-5.111.0.tgz +0 -0
  161. package/components/tryghost-recommendations-5.111.0.tgz +0 -0
  162. package/components/tryghost-referrers-5.111.0.tgz +0 -0
  163. package/components/tryghost-session-service-5.111.0.tgz +0 -0
  164. package/components/tryghost-stats-service-5.111.0.tgz +0 -0
  165. package/components/tryghost-version-notifications-data-service-5.111.0.tgz +0 -0
  166. package/core/demo.js +0 -6
  167. package/core/demo.ts +0 -3
@@ -42,28 +42,35 @@
42
42
  background: rgba(135, 85, 236, 0.12);
43
43
  }
44
44
 
45
- .kg-cta-sponsor-label {
46
- margin: 0 2.4rem;
47
- padding: 1.2rem 0;
45
+ .kg-cta-sponsor-label-wrapper {
46
+ margin: 0 1.5em;
47
+ padding: .7em 0;
48
48
  border-bottom: 1px solid rgba(124, 139, 154, 0.2);
49
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
50
- font-size: 12px;
51
- font-weight: 600;
52
- text-transform: uppercase;
53
- text-wrap: pretty;
54
49
  }
55
50
 
56
51
  @media (max-width: 600px) {
57
- .kg-cta-sponsor-label {
58
- margin: 0 2rem;
59
- padding: 1rem 0;
52
+ .kg-cta-sponsor-label-wrapper {
53
+ margin: 0 1.25em;
54
+ padding: .5em 0;
60
55
  }
61
56
  }
62
57
 
63
- .kg-cta-bg-none .kg-cta-sponsor-label {
58
+ .kg-cta-bg-none .kg-cta-sponsor-label-wrapper {
64
59
  margin: 0;
60
+ padding-top: 0;
61
+ }
62
+
63
+ .kg-cta-has-img .kg-cta-sponsor-label-wrapper:not(.kg-cta-bg-none .kg-cta-sponsor-label-wrapper):not(.kg-cta-minimal .kg-cta-sponsor-label-wrapper) {
64
+ border-bottom: 0;
65
65
  }
66
66
 
67
+ .kg-cta-sponsor-label {
68
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
69
+ font-size: 12px;
70
+ font-weight: 600;
71
+ text-transform: uppercase;
72
+ text-wrap: pretty;
73
+ }
67
74
 
68
75
  .kg-cta-sponsor-label p span:not(a span) {
69
76
  color: color-mix(in srgb, currentColor 45%, transparent);
@@ -75,29 +82,33 @@
75
82
 
76
83
  .kg-cta-content {
77
84
  display: flex;
78
- padding: 2.4rem;
79
- gap: 2.4rem;
85
+ padding: 1.5em;
86
+ gap: 1.5em;
80
87
  }
81
88
 
82
89
  @media (max-width: 600px) {
83
90
  .kg-cta-content {
84
- padding: 2rem;
85
- gap: 2rem;
91
+ padding: 1.25em;
92
+ gap: 1.25em;
86
93
  }
87
94
  }
88
95
 
96
+ .kg-cta-has-img .kg-cta-sponsor-label-wrapper + .kg-cta-content:not(.kg-cta-bg-none .kg-cta-content):not(.kg-cta-minimal .kg-cta-content) {
97
+ padding-top: 0;
98
+ }
99
+
89
100
  .kg-cta-bg-none .kg-cta-content {
90
- padding: 2.4rem 0;
101
+ padding: 1.5em 0;
91
102
  border-bottom: 1px solid rgba(124, 139, 154, 0.2);
92
103
  }
93
104
 
94
- .kg-cta-bg-none .kg-cta-content:not(.kg-cta-sponsor-label + .kg-cta-content) {
105
+ .kg-cta-bg-none .kg-cta-content:not(.kg-cta-sponsor-label-wrapper + .kg-cta-content) {
95
106
  border-top: 1px solid rgba(124, 139, 154, 0.2);
96
107
  }
97
108
 
98
109
  @media (max-width: 600px) {
99
110
  .kg-cta-bg-none .kg-cta-content {
100
- padding: 2rem 0;
111
+ padding: 1.25em 0;
101
112
  }
102
113
  }
103
114
 
@@ -119,12 +130,12 @@
119
130
  .kg-cta-content-inner {
120
131
  display: flex;
121
132
  flex-direction: column;
122
- gap: 2.4rem;
133
+ gap: 1.5em;
123
134
  }
124
135
 
125
136
  @media (max-width: 600px) {
126
137
  .kg-cta-content-inner {
127
- gap: 2rem;
138
+ gap: 1.25em;
128
139
  }
129
140
  }
130
141
 
@@ -133,6 +144,7 @@
133
144
  }
134
145
 
135
146
  .kg-cta-image-container img {
147
+ margin: 0;
136
148
  object-fit: cover;
137
149
  border-radius: 6px;
138
150
  }
@@ -150,16 +162,13 @@
150
162
  }
151
163
  }
152
164
 
153
- .kg-cta-immersive .kg-cta-text {
154
- text-align: center;
155
- }
156
-
157
165
  .kg-cta-text p {
166
+ margin: 0;
158
167
  text-wrap: pretty;
159
168
  }
160
169
 
161
170
  .kg-cta-text p + p {
162
- margin-top: 2rem;
171
+ margin-top: 1.25em;
163
172
  }
164
173
 
165
174
  .kg-cta-text a {
@@ -171,7 +180,10 @@ a.kg-cta-button {
171
180
  position: static;
172
181
  align-items: center;
173
182
  justify-content: center;
183
+ padding: 0 1em;
184
+ height: 2.5em;
174
185
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
186
+ font-size: 0.95em;
175
187
  font-weight: 500;
176
188
  line-height: 1.65;
177
189
  text-decoration: none;
@@ -179,16 +191,6 @@ a.kg-cta-button {
179
191
  transition: opacity 0.2s ease-in-out;
180
192
  }
181
193
 
182
- .kg-cta-minimal a.kg-cta-button {
183
- padding: 0 2rem;
184
- height: 2.4em;
185
- font-size: 0.95em;
186
- }
187
-
188
- .kg-cta-immersive a.kg-cta-button {
189
- padding: .8rem 2rem;
190
- }
191
-
192
194
  a.kg-cta-button:hover {
193
195
  opacity: 0.85;
194
196
  }
@@ -0,0 +1,54 @@
1
+ const TTLCache = require('@isaacs/ttlcache');
2
+ const Base = require('@tryghost/adapter-base-cache');
3
+
4
+ /**
5
+ * Cache adapter compatible wrapper around TTLCache
6
+ * Distinct features of this cache adapter:
7
+ * - it is in-memory only
8
+ * - it supports time-to-live (TTL)
9
+ * - it supports a max number of items
10
+ */
11
+ class AdapterCacheMemoryTTL extends Base {
12
+ #cache;
13
+
14
+ /**
15
+ *
16
+ * @param {Object} [deps]
17
+ * @param {Number} [deps.max] - The max number of items to keep in the cache.
18
+ * @param {Number} [deps.ttl] - The max time in ms to store items
19
+ */
20
+ constructor({max = Infinity, ttl = Infinity} = {}) {
21
+ super();
22
+
23
+ this.#cache = new TTLCache({max, ttl});
24
+ }
25
+
26
+ get(key) {
27
+ return this.#cache.get(key);
28
+ }
29
+
30
+ /**
31
+ *
32
+ * @param {String} key
33
+ * @param {*} value
34
+ * @param {Object} [options]
35
+ * @param {Number} [options.ttl]
36
+ */
37
+ set(key, value, {ttl} = {}) {
38
+ this.#cache.set(key, value, {ttl});
39
+ }
40
+
41
+ reset() {
42
+ this.#cache.clear();
43
+ }
44
+
45
+ /**
46
+ * Helper method to assist "getAll" type of operations
47
+ * @returns {Array<String>} all keys present in the cache
48
+ */
49
+ keys() {
50
+ return [...this.#cache.keys()];
51
+ }
52
+ }
53
+
54
+ module.exports = AdapterCacheMemoryTTL;
@@ -1,3 +1,3 @@
1
- const TTLMemoryCache = require('@tryghost/adapter-cache-memory-ttl');
1
+ const TTLMemoryCache = require('./AdapterCacheMemoryTTL');
2
2
 
3
3
  module.exports = TTLMemoryCache;
@@ -76,7 +76,8 @@ const EDITABLE_SETTINGS = [
76
76
  'body_font',
77
77
  'heading_font',
78
78
  'blocked_email_domains',
79
- 'captcha_enabled'
79
+ 'captcha_enabled',
80
+ 'require_email_mfa'
80
81
  ];
81
82
 
82
83
  module.exports = {
@@ -47,7 +47,8 @@ const keyGroupMapping = {
47
47
  stripe_connect_integration: 'members',
48
48
  portal_name: 'portal',
49
49
  portal_button: 'portal',
50
- portal_plans: 'portal'
50
+ portal_plans: 'portal',
51
+ require_email_mfa: 'security'
51
52
  };
52
53
 
53
54
  const mapKeyToGroup = (key) => {
@@ -54,7 +54,8 @@ const keyTypeMapping = {
54
54
  stripe_connect_livemode: 'boolean',
55
55
  labs: 'object',
56
56
  unsplash: 'object',
57
- bulk_email_settings: 'object'
57
+ bulk_email_settings: 'object',
58
+ require_email_mfa: 'boolean'
58
59
  };
59
60
 
60
61
  const mapKeyToType = (key) => {
@@ -0,0 +1,8 @@
1
+ const {addSetting} = require('../../utils');
2
+
3
+ module.exports = addSetting({
4
+ key: 'require_email_mfa',
5
+ value: 'false',
6
+ type: 'boolean',
7
+ group: 'security'
8
+ });
@@ -0,0 +1,31 @@
1
+ const logging = require('@tryghost/logging');
2
+ const {default: ObjectID} = require('bson-objectid');
3
+ const {createTransactionalMigration, meta} = require('../../utils');
4
+
5
+ module.exports = createTransactionalMigration(
6
+ async function up(knex) {
7
+ logging.info('Creating "Super Editor" role');
8
+ const existingRole = await knex('roles').where({
9
+ name: 'Super Editor'
10
+ }).first();
11
+
12
+ if (existingRole) {
13
+ logging.warn('"Super Editor" role already exists, skipping');
14
+ return;
15
+ }
16
+
17
+ await knex('roles').insert({
18
+ id: (new ObjectID()).toHexString(),
19
+ name: 'Super Editor',
20
+ description: 'Editor plus member management',
21
+ created_by: meta.MIGRATION_USER,
22
+ created_at: knex.raw('current_timestamp')
23
+ });
24
+ },
25
+ async function down(knex) {
26
+ logging.info('Deleting role "Super Editor"');
27
+ await knex('roles').where({
28
+ name: 'Super Editor'
29
+ }).del();
30
+ }
31
+ );
@@ -0,0 +1,291 @@
1
+ const {addPermissionToRole, combineTransactionalMigrations} = require('../../utils');
2
+ module.exports = combineTransactionalMigrations(
3
+ addPermissionToRole({
4
+ permission: 'Browse Members',
5
+ role: 'Super Editor'
6
+ }),
7
+ addPermissionToRole({
8
+ permission: 'Read Members',
9
+ role: 'Super Editor'
10
+ }),
11
+ addPermissionToRole({
12
+ permission: 'Edit Members',
13
+ role: 'Super Editor'
14
+ }),
15
+ addPermissionToRole({
16
+ permission: 'Add Members',
17
+ role: 'Super Editor'
18
+ }),
19
+ addPermissionToRole({
20
+ permission: 'Delete Members',
21
+ role: 'Super Editor'
22
+ }),
23
+ addPermissionToRole({
24
+ permission: 'Read offers',
25
+ role: 'Super Editor'
26
+ }),
27
+ addPermissionToRole({
28
+ permission: 'Browse offers',
29
+ role: 'Super Editor'
30
+ }),
31
+ addPermissionToRole({
32
+ permission: 'Read member signin urls',
33
+ role: 'Super Editor'
34
+ }),
35
+ addPermissionToRole({
36
+ permission: 'Browse notifications',
37
+ role: 'Super Editor'
38
+ }),
39
+ addPermissionToRole({
40
+ permission: 'Add notifications',
41
+ role: 'Super Editor'
42
+ }),
43
+ addPermissionToRole({
44
+ permission: 'Delete notifications',
45
+ role: 'Super Editor'
46
+ }),
47
+ addPermissionToRole({
48
+ permission: 'Generate slugs',
49
+ role: 'Super Editor'
50
+ }),
51
+ addPermissionToRole({
52
+ permission: 'Browse posts',
53
+ role: 'Super Editor'
54
+ }),
55
+ addPermissionToRole({
56
+ permission: 'Read posts',
57
+ role: 'Super Editor'
58
+ }),
59
+ addPermissionToRole({
60
+ permission: 'Edit posts',
61
+ role: 'Super Editor'
62
+ }),
63
+ addPermissionToRole({
64
+ permission: 'Add posts',
65
+ role: 'Super Editor'
66
+ }),
67
+ addPermissionToRole({
68
+ permission: 'Delete posts',
69
+ role: 'Super Editor'
70
+ }),
71
+ addPermissionToRole({
72
+ permission: 'Publish posts',
73
+ role: 'Super Editor'
74
+ }),
75
+ addPermissionToRole({
76
+ permission: 'Browse settings',
77
+ role: 'Super Editor'
78
+ }),
79
+ addPermissionToRole({
80
+ permission: 'Read settings',
81
+ role: 'Super Editor'
82
+ }),
83
+ addPermissionToRole({
84
+ permission: 'Browse tags',
85
+ role: 'Super Editor'
86
+ }),
87
+ addPermissionToRole({
88
+ permission: 'Read tags',
89
+ role: 'Super Editor'
90
+ }),
91
+ addPermissionToRole({
92
+ permission: 'Edit tags',
93
+ role: 'Super Editor'
94
+ }),
95
+ addPermissionToRole({
96
+ permission: 'Add tags',
97
+ role: 'Super Editor'
98
+ }),
99
+ addPermissionToRole({
100
+ permission: 'Delete tags',
101
+ role: 'Super Editor'
102
+ }),
103
+ addPermissionToRole({
104
+ permission: 'Browse themes',
105
+ role: 'Super Editor'
106
+ }),
107
+ addPermissionToRole({
108
+ permission: 'View active theme details',
109
+ role: 'Super Editor'
110
+ }),
111
+ addPermissionToRole({
112
+ permission: 'Browse users',
113
+ role: 'Super Editor'
114
+ }),
115
+ addPermissionToRole({
116
+ permission: 'Read users',
117
+ role: 'Super Editor'
118
+ }),
119
+ addPermissionToRole({
120
+ permission: 'Edit users',
121
+ role: 'Super Editor'
122
+ }),
123
+ addPermissionToRole({
124
+ permission: 'Add users',
125
+ role: 'Super Editor'
126
+ }),
127
+ addPermissionToRole({
128
+ permission: 'Delete users',
129
+ role: 'Super Editor'
130
+ }),
131
+ addPermissionToRole({
132
+ permission: 'Assign a role',
133
+ role: 'Super Editor'
134
+ }),
135
+ addPermissionToRole({
136
+ permission: 'Browse roles',
137
+ role: 'Super Editor'
138
+ }),
139
+ addPermissionToRole({
140
+ permission: 'Browse invites',
141
+ role: 'Super Editor'
142
+ }),
143
+ addPermissionToRole({
144
+ permission: 'Read invites',
145
+ role: 'Super Editor'
146
+ }),
147
+ addPermissionToRole({
148
+ permission: 'Add invites',
149
+ role: 'Super Editor'
150
+ }),
151
+ addPermissionToRole({
152
+ permission: 'Delete invites',
153
+ role: 'Super Editor'
154
+ }),
155
+ addPermissionToRole({
156
+ permission: 'Edit invites',
157
+ role: 'Super Editor'
158
+ }),
159
+ addPermissionToRole({
160
+ permission: 'Email preview',
161
+ role: 'Super Editor'
162
+ }),
163
+ addPermissionToRole({
164
+ permission: 'Send test email',
165
+ role: 'Super Editor'
166
+ }),
167
+ addPermissionToRole({
168
+ permission: 'Read emails',
169
+ role: 'Super Editor'
170
+ }),
171
+ addPermissionToRole({
172
+ permission: 'Browse emails',
173
+ role: 'Super Editor'
174
+ }),
175
+ addPermissionToRole({
176
+ permission: 'Retry emails',
177
+ role: 'Super Editor'
178
+ }),
179
+ addPermissionToRole({
180
+ permission: 'Browse snippets',
181
+ role: 'Super Editor'
182
+ }),
183
+ addPermissionToRole({
184
+ permission: 'Read snippets',
185
+ role: 'Super Editor'
186
+ }),
187
+ addPermissionToRole({
188
+ permission: 'Edit snippets',
189
+ role: 'Super Editor'
190
+ }),
191
+ addPermissionToRole({
192
+ permission: 'Add snippets',
193
+ role: 'Super Editor'
194
+ }),
195
+ addPermissionToRole({
196
+ permission: 'Delete snippets',
197
+ role: 'Super Editor'
198
+ }),
199
+ addPermissionToRole({
200
+ permission: 'Browse labels',
201
+ role: 'Super Editor'
202
+ }),
203
+ addPermissionToRole({
204
+ permission: 'Read labels',
205
+ role: 'Super Editor'
206
+ }),
207
+ addPermissionToRole({
208
+ permission: 'Edit labels',
209
+ role: 'Super Editor'
210
+ }),
211
+ addPermissionToRole({
212
+ permission: 'Add labels',
213
+ role: 'Super Editor'
214
+ }),
215
+ addPermissionToRole({
216
+ permission: 'Delete labels',
217
+ role: 'Super Editor'
218
+ }),
219
+ addPermissionToRole({
220
+ permission: 'Browse Products',
221
+ role: 'Super Editor'
222
+ }),
223
+ addPermissionToRole({
224
+ permission: 'Read Products',
225
+ role: 'Super Editor'
226
+ }),
227
+ addPermissionToRole({
228
+ permission: 'Browse newsletters',
229
+ role: 'Super Editor'
230
+ }),
231
+ addPermissionToRole({
232
+ permission: 'Read newsletters',
233
+ role: 'Super Editor'
234
+ }),
235
+ addPermissionToRole({
236
+ permission: 'Browse collections',
237
+ role: 'Super Editor'
238
+ }),
239
+ addPermissionToRole({
240
+ permission: 'Read collections',
241
+ role: 'Super Editor'
242
+ }),
243
+ addPermissionToRole({
244
+ permission: 'Edit collections',
245
+ role: 'Super Editor'
246
+ }),
247
+ addPermissionToRole({
248
+ permission: 'Add collections',
249
+ role: 'Super Editor'
250
+ }),
251
+ addPermissionToRole({
252
+ permission: 'Delete collections',
253
+ role: 'Super Editor'
254
+ }),
255
+ addPermissionToRole({
256
+ permission: 'Moderate comments',
257
+ role: 'Super Editor'
258
+ }),
259
+ addPermissionToRole({
260
+ permission: 'Like comments',
261
+ role: 'Super Editor'
262
+ }),
263
+ addPermissionToRole({
264
+ permission: 'Unlike comments',
265
+ role: 'Super Editor'
266
+ }),
267
+ addPermissionToRole({
268
+ permission: 'Add comments',
269
+ role: 'Super Editor'
270
+ }),
271
+ addPermissionToRole({
272
+ permission: 'Edit comments',
273
+ role: 'Super Editor'
274
+ }),
275
+ addPermissionToRole({
276
+ permission: 'Delete comments',
277
+ role: 'Super Editor'
278
+ }),
279
+ addPermissionToRole({
280
+ permission: 'Read comments',
281
+ role: 'Super Editor'
282
+ }),
283
+ addPermissionToRole({
284
+ permission: 'Browse comments',
285
+ role: 'Super Editor'
286
+ }),
287
+ addPermissionToRole({
288
+ permission: 'Report comments',
289
+ role: 'Super Editor'
290
+ })
291
+ );
@@ -603,5 +603,11 @@
603
603
  },
604
604
  "type": "boolean"
605
605
  }
606
+ },
607
+ "security": {
608
+ "require_email_mfa": {
609
+ "defaultValue": false,
610
+ "type": "boolean"
611
+ }
606
612
  }
607
613
  }
@@ -106,6 +106,10 @@
106
106
  {
107
107
  "name": "Scheduler Integration",
108
108
  "description": "Internal Scheduler Client"
109
+ },
110
+ {
111
+ "name": "Super Editor",
112
+ "description": "Super Editors"
109
113
  }
110
114
  ]
111
115
  },
@@ -917,6 +921,29 @@
917
921
  "recommendation": "all",
918
922
  "member_signin_url": "read"
919
923
  },
924
+ "Super Editor": {
925
+ "notification": "all",
926
+ "post": "all",
927
+ "setting": ["browse", "read"],
928
+ "slug": "all",
929
+ "tag": "all",
930
+ "user": "all",
931
+ "role": "all",
932
+ "invite": "all",
933
+ "theme": ["browse", "readActive"],
934
+ "email_preview": "all",
935
+ "email": "all",
936
+ "snippet": "all",
937
+ "label": ["browse", "read", "edit", "add", "destroy"],
938
+ "product": ["browse", "read"],
939
+ "newsletter": ["browse", "read"],
940
+ "collection": "all",
941
+ "recommendation": ["browse", "read"],
942
+ "member": ["browse", "read", "add", "edit", "destroy"],
943
+ "member_signin_url": "read",
944
+ "offer": ["browse", "read"],
945
+ "comment": "all"
946
+ },
920
947
  "Editor": {
921
948
  "notification": "all",
922
949
  "post": "all",
@@ -1,4 +1,3 @@
1
- const _ = require('lodash');
2
1
  const tpl = require('@tryghost/tpl');
3
2
  const errors = require('@tryghost/errors');
4
3
  const constants = require('@tryghost/constants');
@@ -6,7 +5,7 @@ const security = require('@tryghost/security');
6
5
  const settingsCache = require('../../shared/settings-cache');
7
6
  const limitService = require('../services/limits');
8
7
  const ghostBookshelf = require('./base');
9
-
8
+ const {setIsRoles} = require('./role-utils');
10
9
  const messages = {
11
10
  notEnoughPermission: 'You do not have permission to perform this action',
12
11
  roleNotFound: 'Role not found',
@@ -86,14 +85,14 @@ Invite = ghostBookshelf.Model.extend({
86
85
 
87
86
  let allowed = [];
88
87
  if (loadedPermissions.user) {
89
- if (_.some(loadedPermissions.user.roles, {name: 'Owner'}) ||
90
- _.some(loadedPermissions.user.roles, {name: 'Administrator'})) {
91
- allowed = ['Administrator', 'Editor', 'Author', 'Contributor'];
92
- } else if (_.some(loadedPermissions.user.roles, {name: 'Editor'})) {
88
+ const {isOwner, isAdmin, isEitherEditor} = setIsRoles(loadedPermissions);
89
+ if (isOwner || isAdmin) {
90
+ allowed = ['Administrator', 'Editor', 'Author', 'Contributor', 'Super Editor'];
91
+ } else if (isEitherEditor) {
93
92
  allowed = ['Author', 'Contributor'];
94
93
  }
95
94
  } else if (loadedPermissions.apiKey) {
96
- allowed = ['Editor', 'Author', 'Contributor'];
95
+ allowed = ['Editor', 'Author', 'Contributor', 'Super Editor'];
97
96
  }
98
97
 
99
98
  if (allowed.indexOf(roleToInvite.get('name')) === -1) {