├── .csscomb.json ├── .editorconfig ├── .ember-cli ├── .eslintignore ├── .eslintrc.js ├── .github ├── CONTRIBUTING.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── test.yml ├── .gitignore ├── .lint-todo ├── .lint-todorc.js ├── .template-lintrc.js ├── .watchmanconfig ├── Gruntfile.js ├── LICENSE ├── README.md ├── SECURITY.md ├── app ├── README.md ├── adapters │ ├── api-key.js │ ├── application.js │ ├── base.js │ ├── custom-theme-setting-list.js │ ├── email.js │ ├── embedded-relation-adapter.js │ ├── label.js │ ├── member.js │ ├── newsletter.js │ ├── offer.js │ ├── page.js │ ├── post.js │ ├── setting.js │ ├── tag.js │ ├── theme.js │ ├── tier.js │ └── user.js ├── app.js ├── authenticators │ └── cookie.js ├── components │ ├── aspect-ratio-box.hbs │ ├── aspect-ratio-box.js │ ├── custom-theme-settings │ │ ├── boolean.hbs │ │ ├── boolean.js │ │ ├── color.hbs │ │ ├── color.js │ │ ├── image.hbs │ │ ├── image.js │ │ ├── select.hbs │ │ ├── select.js │ │ ├── text.hbs │ │ └── text.js │ ├── dashboard │ │ ├── charts │ │ │ ├── anchor.hbs │ │ │ ├── anchor.js │ │ │ ├── engagement.hbs │ │ │ ├── engagement.js │ │ │ ├── overview.hbs │ │ │ ├── overview.js │ │ │ ├── paid-breakdown.hbs │ │ │ ├── paid-breakdown.js │ │ │ ├── paid-mix.hbs │ │ │ ├── paid-mix.js │ │ │ ├── paid-mrr.hbs │ │ │ ├── paid-mrr.js │ │ │ ├── recents.hbs │ │ │ └── recents.js │ │ ├── parts │ │ │ ├── metric.hbs │ │ │ ├── percentage.hbs │ │ │ └── zero.hbs │ │ ├── prototype │ │ │ ├── control-panel.hbs │ │ │ └── control-panel.js │ │ └── resources │ │ │ ├── community.hbs │ │ │ ├── newsletter.hbs │ │ │ ├── newsletter.js │ │ │ ├── resources.hbs │ │ │ ├── resources.js │ │ │ ├── staff-picks.hbs │ │ │ ├── staff-picks.js │ │ │ ├── whats-new.hbs │ │ │ └── whats-new.js │ ├── editor │ │ ├── modals │ │ │ ├── preview.hbs │ │ │ ├── preview.js │ │ │ ├── preview │ │ │ │ ├── browser.hbs │ │ │ │ ├── browser.js │ │ │ │ ├── email.hbs │ │ │ │ ├── email.js │ │ │ │ ├── mobile.hbs │ │ │ │ ├── mobile.js │ │ │ │ ├── social.hbs │ │ │ │ └── social.js │ │ │ ├── publish-flow.hbs │ │ │ ├── publish-flow.js │ │ │ ├── publish-flow │ │ │ │ ├── complete-with-email-error.hbs │ │ │ │ ├── complete-with-email-error.js │ │ │ │ ├── complete.hbs │ │ │ │ ├── confirm.hbs │ │ │ │ ├── confirm.js │ │ │ │ ├── options.hbs │ │ │ │ └── options.js │ │ │ ├── update-flow.hbs │ │ │ └── update-flow.js │ │ ├── publish-buttons.hbs │ │ ├── publish-management.hbs │ │ ├── publish-management.js │ │ └── publish-options │ │ │ ├── email-recipients.hbs │ │ │ ├── publish-at.hbs │ │ │ ├── publish-at.js │ │ │ ├── publish-type.hbs │ │ │ └── publish-type.js │ ├── epm-modal-container.hbs │ ├── epm-modal-container.js │ ├── gh-alert.hbs │ ├── gh-alert.js │ ├── gh-alerts.hbs │ ├── gh-alerts.js │ ├── gh-app.hbs │ ├── gh-basic-dropdown.hbs │ ├── gh-basic-dropdown.js │ ├── gh-benefit-item.hbs │ ├── gh-benefit-item.js │ ├── gh-billing-iframe.hbs │ ├── gh-billing-iframe.js │ ├── gh-billing-modal.hbs │ ├── gh-billing-modal.js │ ├── gh-billing-update-button.hbs │ ├── gh-billing-update-button.js │ ├── gh-blog-url.hbs │ ├── gh-blog-url.js │ ├── gh-brand-settings-form.hbs │ ├── gh-brand-settings-form.js │ ├── gh-browser-preview.hbs │ ├── gh-browser-preview.js │ ├── gh-canvas-header.hbs │ ├── gh-canvas-header.js │ ├── gh-cm-editor.hbs │ ├── gh-cm-editor.js │ ├── gh-content-cover.js │ ├── gh-contentfilter.hbs │ ├── gh-contentfilter.js │ ├── gh-custom-view-title.hbs │ ├── gh-custom-view-title.js │ ├── gh-date-picker.hbs │ ├── gh-date-picker.js │ ├── gh-date-time-picker.hbs │ ├── gh-date-time-picker.js │ ├── gh-dropdown-button.js │ ├── gh-dropdown.js │ ├── gh-editor-feature-image.hbs │ ├── gh-editor-feature-image.js │ ├── gh-editor-post-status.hbs │ ├── gh-editor-post-status.js │ ├── gh-editor.hbs │ ├── gh-editor.js │ ├── gh-email-preview-link.hbs │ ├── gh-email-preview-link.js │ ├── gh-error-message.hbs │ ├── gh-error-message.js │ ├── gh-feature-flag.hbs │ ├── gh-feature-flag.js │ ├── gh-file-input.js │ ├── gh-file-upload.hbs │ ├── gh-file-upload.js │ ├── gh-file-uploader.hbs │ ├── gh-file-uploader.js │ ├── gh-font-selector.hbs │ ├── gh-font-selector.js │ ├── gh-form-group.js │ ├── gh-fullscreen-modal.hbs │ ├── gh-fullscreen-modal.js │ ├── gh-html-iframe.hbs │ ├── gh-html-iframe.js │ ├── gh-image-uploader-with-preview.hbs │ ├── gh-image-uploader.hbs │ ├── gh-image-uploader.js │ ├── gh-infinity-loader.hbs │ ├── gh-infinity-loader.js │ ├── gh-input-with-select │ │ ├── index.hbs │ │ ├── index.js │ │ ├── suggested-option.hbs │ │ ├── trigger.hbs │ │ └── trigger.js │ ├── gh-koenig-editor-react.hbs │ ├── gh-koenig-editor-react.js │ ├── gh-koenig-editor.hbs │ ├── gh-koenig-editor.js │ ├── gh-launch-wizard │ │ ├── connect-stripe.hbs │ │ ├── connect-stripe.js │ │ ├── customise-design.hbs │ │ ├── customise-design.js │ │ ├── finalise.hbs │ │ ├── finalise.js │ │ ├── set-pricing.hbs │ │ └── set-pricing.js │ ├── gh-link-to-custom-views-index.hbs │ ├── gh-link-to-custom-views-index.js │ ├── gh-loading-list.hbs │ ├── gh-loading-spinner.hbs │ ├── gh-loading-spinner.js │ ├── gh-markdown-editor.hbs │ ├── gh-markdown-editor.js │ ├── gh-member-avatar.hbs │ ├── gh-member-avatar.js │ ├── gh-member-details-activity.hbs │ ├── gh-member-details.hbs │ ├── gh-member-details.js │ ├── gh-member-label-input.hbs │ ├── gh-member-label-input.js │ ├── gh-member-settings-form.hbs │ ├── gh-member-settings-form.js │ ├── gh-member-single-label-input.hbs │ ├── gh-member-single-label-input.js │ ├── gh-members-filter-count.hbs │ ├── gh-members-filter-count.js │ ├── gh-members-import-mapping-input.hbs │ ├── gh-members-import-mapping-input.js │ ├── gh-members-import-table.hbs │ ├── gh-members-import-table.js │ ├── gh-members-list-item-column.hbs │ ├── gh-members-list-item-column.js │ ├── gh-members-list-item.hbs │ ├── gh-members-list-item.js │ ├── gh-members-no-members.hbs │ ├── gh-members-no-members.js │ ├── gh-members-payments-setting.hbs │ ├── gh-members-payments-setting.js │ ├── gh-members-recipient-select.hbs │ ├── gh-members-recipient-select.js │ ├── gh-members-segment-count.hbs │ ├── gh-members-segment-count.js │ ├── gh-members-segment-select.hbs │ ├── gh-members-segment-select.js │ ├── gh-membership-tiers-alpha.hbs │ ├── gh-membership-tiers-alpha.js │ ├── gh-mobile-nav-bar.hbs │ ├── gh-mobile-nav-bar.js │ ├── gh-nav-menu.hbs │ ├── gh-nav-menu.js │ ├── gh-nav-menu │ │ ├── design.hbs │ │ ├── design.js │ │ ├── footer.hbs │ │ ├── footer.js │ │ ├── main.hbs │ │ └── main.js │ ├── gh-navitem-url-input.js │ ├── gh-navitem.hbs │ ├── gh-navitem.js │ ├── gh-notification.hbs │ ├── gh-notification.js │ ├── gh-notifications.hbs │ ├── gh-notifications.js │ ├── gh-portal-links.hbs │ ├── gh-portal-links.js │ ├── gh-post-bookmark.hbs │ ├── gh-post-settings-menu.hbs │ ├── gh-post-settings-menu.js │ ├── gh-post-settings-menu │ │ ├── email.hbs │ │ ├── email.js │ │ ├── visibility-segment-select.hbs │ │ └── visibility-segment-select.js │ ├── gh-posts-list-item.hbs │ ├── gh-posts-list-item.js │ ├── gh-power-select │ │ ├── trigger.hbs │ │ └── trigger.js │ ├── gh-progress-bar.hbs │ ├── gh-progress-bar.js │ ├── gh-psm-authors-input.hbs │ ├── gh-psm-authors-input.js │ ├── gh-psm-tags-input.hbs │ ├── gh-psm-tags-input.js │ ├── gh-psm-template-select.hbs │ ├── gh-psm-template-select.js │ ├── gh-psm-visibility-input.hbs │ ├── gh-psm-visibility-input.js │ ├── gh-recipient-filter-count.hbs │ ├── gh-role-selection.hbs │ ├── gh-role-selection.js │ ├── gh-scroll-trigger.hbs │ ├── gh-scroll-trigger.js │ ├── gh-search-input.hbs │ ├── gh-search-input.js │ ├── gh-simplemde.hbs │ ├── gh-simplemde.js │ ├── gh-site-iframe.hbs │ ├── gh-site-iframe.js │ ├── gh-skip-link.js │ ├── gh-tag-settings-form.hbs │ ├── gh-tag-settings-form.js │ ├── gh-tags-list-item.hbs │ ├── gh-task-button.hbs │ ├── gh-task-button.js │ ├── gh-text-input.hbs │ ├── gh-text-input.js │ ├── gh-textarea.js │ ├── gh-theme-error-li.hbs │ ├── gh-theme-error-li.js │ ├── gh-theme-table.hbs │ ├── gh-theme-table.js │ ├── gh-tier-card.hbs │ ├── gh-tier-card.js │ ├── gh-tiers-price-billingperiod.hbs │ ├── gh-tiers-price-billingperiod.js │ ├── gh-timezone-select.hbs │ ├── gh-timezone-select.js │ ├── gh-token-input.hbs │ ├── gh-token-input.js │ ├── gh-token-input │ │ ├── label-selected-item.hbs │ │ ├── label-token.hbs │ │ ├── label-token.js │ │ ├── select-multiple.hbs │ │ ├── select-multiple.js │ │ ├── suggested-option.hbs │ │ ├── tag-token.hbs │ │ ├── tag-token.js │ │ ├── trigger.hbs │ │ └── trigger.js │ ├── gh-trim-focus-input.js │ ├── gh-unsplash-photo.hbs │ ├── gh-unsplash-photo.js │ ├── gh-unsplash.hbs │ ├── gh-unsplash.js │ ├── gh-uploader.hbs │ ├── gh-uploader.js │ ├── gh-url-input.hbs │ ├── gh-url-input.js │ ├── gh-url-preview.hbs │ ├── gh-url-preview.js │ ├── gh-user-active.hbs │ ├── gh-user-active.js │ ├── gh-user-invited.hbs │ ├── gh-user-invited.js │ ├── gh-user-list-item.hbs │ ├── gh-user-list-item.js │ ├── gh-validation-status-container.js │ ├── gh-view-title.hbs │ ├── gh-view-title.js │ ├── inputs │ │ ├── select.hbs │ │ └── select │ │ │ └── option.hbs │ ├── koenig-react-editor.hbs │ ├── koenig-react-editor.js │ ├── liquid-container.js │ ├── member │ │ ├── activity-feed-empty.hbs │ │ ├── activity-feed.hbs │ │ ├── newsletter-preference.hbs │ │ └── newsletter-preference.js │ ├── members-activity │ │ ├── event-type-filter.hbs │ │ ├── event-type-filter.js │ │ ├── member-filter-trigger.hbs │ │ ├── member-filter.hbs │ │ ├── member-filter.js │ │ ├── no-events.hbs │ │ ├── table-row.hbs │ │ └── table.hbs │ ├── members │ │ ├── filter-value.hbs │ │ ├── filter-value.js │ │ ├── filter.hbs │ │ └── filter.js │ ├── modal-base.js │ ├── modal-delete-all.hbs │ ├── modal-delete-all.js │ ├── modal-delete-integration.hbs │ ├── modal-delete-integration.js │ ├── modal-delete-member.hbs │ ├── modal-delete-member.js │ ├── modal-delete-snippet.hbs │ ├── modal-delete-snippet.js │ ├── modal-delete-tag.hbs │ ├── modal-delete-tag.js │ ├── modal-delete-user.hbs │ ├── modal-delete-user.js │ ├── modal-delete-webhook.hbs │ ├── modal-delete-webhook.js │ ├── modal-disconnect-stripe.hbs │ ├── modal-disconnect-stripe.js │ ├── modal-early-access.hbs │ ├── modal-early-access.js │ ├── modal-email-design-settings.hbs │ ├── modal-email-design-settings.js │ ├── modal-free-membership-settings.hbs │ ├── modal-free-membership-settings.js │ ├── modal-impersonate-member.hbs │ ├── modal-impersonate-member.js │ ├── modal-import-members.hbs │ ├── modal-import-members.js │ ├── modal-import-members │ │ ├── csv-file-mapping.hbs │ │ ├── csv-file-mapping.js │ │ ├── csv-file-select.hbs │ │ └── csv-file-select.js │ ├── modal-invite-new-user.hbs │ ├── modal-invite-new-user.js │ ├── modal-leave-settings.hbs │ ├── modal-leave-settings.js │ ├── modal-markdown-help.hbs │ ├── modal-markdown-help.js │ ├── modal-member-tier.hbs │ ├── modal-member-tier.js │ ├── modal-members-label-form.hbs │ ├── modal-members-label-form.js │ ├── modal-portal-settings.hbs │ ├── modal-portal-settings.js │ ├── modal-re-authenticate.hbs │ ├── modal-re-authenticate.js │ ├── modal-regenerate-key.hbs │ ├── modal-regenerate-key.js │ ├── modal-regenerate-token.hbs │ ├── modal-regenerate-token.js │ ├── modal-reset-all-passwords.hbs │ ├── modal-reset-all-passwords.js │ ├── modal-select-user-role.hbs │ ├── modal-select-user-role.js │ ├── modal-stripe-connect.hbs │ ├── modal-stripe-connect.js │ ├── modal-suspend-user.hbs │ ├── modal-suspend-user.js │ ├── modal-tier.hbs │ ├── modal-tier.js │ ├── modal-transfer-owner.hbs │ ├── modal-transfer-owner.js │ ├── modal-unsubscribe-members.hbs │ ├── modal-unsubscribe-members.js │ ├── modal-unsuspend-user.hbs │ ├── modal-unsuspend-user.js │ ├── modal-update-snippet.hbs │ ├── modal-update-snippet.js │ ├── modal-upgrade-host-limit.hbs │ ├── modal-upgrade-host-limit.js │ ├── modal-upgrade-unsuspend-user-host-limit.hbs │ ├── modal-upgrade-unsuspend-user-host-limit.js │ ├── modal-upload-image.hbs │ ├── modal-upload-image.js │ ├── modal-webhook-form.hbs │ ├── modal-webhook-form.js │ ├── modal-whats-new.js │ ├── modals │ │ ├── confirm-unsaved-changes.hbs │ │ ├── custom-view-form.hbs │ │ ├── custom-view-form.js │ │ ├── delete-post.hbs │ │ ├── delete-post.js │ │ ├── design │ │ │ ├── confirm-delete-theme.hbs │ │ │ ├── confirm-delete-theme.js │ │ │ ├── install-theme.hbs │ │ │ ├── install-theme.js │ │ │ ├── theme-errors.hbs │ │ │ ├── upload-theme.hbs │ │ │ ├── upload-theme.js │ │ │ ├── view-theme.hbs │ │ │ └── view-theme.js │ │ ├── editor │ │ │ └── confirm-leave.hbs │ │ ├── email-preview.hbs │ │ ├── email-preview.js │ │ ├── limits │ │ │ ├── custom-integration.hbs │ │ │ ├── custom-theme.hbs │ │ │ └── multiple-newsletters.hbs │ │ ├── members │ │ │ ├── bulk-add-label.hbs │ │ │ ├── bulk-add-label.js │ │ │ ├── bulk-delete.hbs │ │ │ ├── bulk-delete.js │ │ │ ├── bulk-remove-label.hbs │ │ │ ├── bulk-remove-label.js │ │ │ ├── bulk-unsubscribe.hbs │ │ │ └── bulk-unsubscribe.js │ │ ├── new-custom-integration.hbs │ │ ├── new-custom-integration.js │ │ ├── newsletters │ │ │ ├── confirm-archive.hbs │ │ │ ├── confirm-newsletter-email.hbs │ │ │ ├── confirm-unarchive.hbs │ │ │ ├── edit.hbs │ │ │ ├── edit.js │ │ │ ├── edit │ │ │ │ ├── design.hbs │ │ │ │ ├── design.js │ │ │ │ ├── preview.hbs │ │ │ │ ├── preview.js │ │ │ │ ├── settings.hbs │ │ │ │ └── settings.js │ │ │ ├── new.hbs │ │ │ ├── new.js │ │ │ ├── verify-newsletter-email.hbs │ │ │ └── verify-newsletter-email.js │ │ ├── offers │ │ │ ├── archive.hbs │ │ │ ├── archive.js │ │ │ ├── link.hbs │ │ │ ├── link.js │ │ │ ├── unarchive.hbs │ │ │ └── unarchive.js │ │ ├── search.hbs │ │ ├── search.js │ │ ├── settings │ │ │ ├── confirm-email.hbs │ │ │ ├── verify-email.hbs │ │ │ └── verify-email.js │ │ └── tiers │ │ │ ├── archive.hbs │ │ │ ├── archive.js │ │ │ ├── unarchive.hbs │ │ │ └── unarchive.js │ ├── power-select-vertical-collection-options.hbs │ ├── power-select-vertical-collection-options.js │ ├── react-component.hbs │ ├── settings │ │ ├── design │ │ │ ├── general-settings-form.hbs │ │ │ └── theme-settings-form.hbs │ │ ├── form-fields │ │ │ ├── accent-color.hbs │ │ │ ├── accent-color.js │ │ │ ├── publication-cover.hbs │ │ │ ├── publication-cover.js │ │ │ ├── publication-icon.hbs │ │ │ ├── publication-icon.js │ │ │ ├── publication-logo.hbs │ │ │ ├── publication-logo.js │ │ │ ├── site-description.hbs │ │ │ └── site-description.js │ │ ├── members-comment-access.hbs │ │ ├── members-comment-access.js │ │ ├── members-default-post-access.hbs │ │ ├── members-default-post-access.js │ │ ├── members-email │ │ │ ├── default-recipients-select.hbs │ │ │ └── default-recipients-select.js │ │ ├── members-subscription-access.hbs │ │ ├── members-subscription-access.js │ │ ├── members │ │ │ ├── archive-tier.hbs │ │ │ └── archive-tier.js │ │ ├── newsletters.hbs │ │ ├── newsletters.js │ │ └── newsletters │ │ │ ├── newsletter-management.hbs │ │ │ └── newsletter-management.js │ └── tiers │ │ ├── segment-select.hbs │ │ └── segment-select.js ├── controllers │ ├── application.js │ ├── billing.js │ ├── dashboard.js │ ├── designsandbox.js │ ├── editor.js │ ├── editor │ │ └── edit-loading.js │ ├── error.js │ ├── explore.js │ ├── home.js │ ├── launch.js │ ├── member.js │ ├── members-activity.js │ ├── members.js │ ├── members │ │ └── import.js │ ├── offer.js │ ├── offers.js │ ├── pages-loading.js │ ├── pages.js │ ├── posts-loading.js │ ├── posts.js │ ├── react-editor.js │ ├── react-editor │ │ └── edit-loading.js │ ├── reset.js │ ├── settings.js │ ├── settings │ │ ├── code-injection.js │ │ ├── design.js │ │ ├── design │ │ │ ├── change-theme.js │ │ │ ├── change-theme │ │ │ │ └── install.js │ │ │ └── index.js │ │ ├── general.js │ │ ├── integration.js │ │ ├── integration │ │ │ └── webhooks │ │ │ │ ├── edit.js │ │ │ │ └── new.js │ │ ├── integrations.js │ │ ├── integrations │ │ │ ├── amp.js │ │ │ ├── firstpromoter.js │ │ │ ├── slack.js │ │ │ ├── unsplash.js │ │ │ └── zapier.js │ │ ├── labs.js │ │ ├── membership.js │ │ ├── navigation.js │ │ ├── newsletters.js │ │ ├── staff │ │ │ ├── index.js │ │ │ ├── user-loading.js │ │ │ └── user.js │ │ ├── tier.js │ │ └── tiers.js │ ├── setup.js │ ├── setup │ │ └── done.js │ ├── signin.js │ ├── signup.js │ ├── site.js │ ├── tag.js │ ├── tags.js │ └── whatsnew.js ├── errors │ ├── email-failed-error.js │ └── member-import-error.js ├── helpers │ ├── accent-color-background.js │ ├── author-names.js │ ├── background-image-style.js │ ├── capitalize-first-letter.js │ ├── currency-symbol.js │ ├── enable-developer-experiments.js │ ├── event-name.js │ ├── feature.js │ ├── first-name.js │ ├── format-number.js │ ├── full-email-address.js │ ├── get-setting.js │ ├── gh-count-characters.js │ ├── gh-count-down-characters.js │ ├── gh-format-post-time.js │ ├── gh-pluralize.js │ ├── gh-price-amount.js │ ├── gh-user-can-admin.js │ ├── hex-adjust.js │ ├── hex-contrast.js │ ├── highlighted-text.js │ ├── humanize-setting-key.js │ ├── integration-icon-style.js │ ├── is-moment-today.js │ ├── members-count-fetcher.js │ ├── members-event-fetcher.js │ ├── members-event-filter.js │ ├── moment-site-tz.js │ ├── most-recently-updated.js │ ├── noop.js │ ├── parse-member-event.js │ ├── post-author-names.js │ ├── publish-options.js │ ├── query-selector.js │ ├── reset-query-params.js │ ├── set-has.js │ ├── set-query-params.js │ ├── site-icon-style.js │ ├── toggle-feature.js │ ├── ui-btn-span.js │ ├── ui-btn.js │ └── ui-text.js ├── index.html ├── initializers │ ├── ember-simple-auth.js │ ├── trailing-hash.js │ └── upgrade-status.js ├── mixins │ ├── body-event-listener.js │ ├── dropdown-mixin.js │ ├── shortcuts-route.js │ ├── shortcuts.js │ ├── text-input.js │ ├── validation-engine.js │ └── validation-state.js ├── models │ ├── action.js │ ├── api-key.js │ ├── base.js │ ├── custom-theme-setting-list.js │ ├── custom-theme-setting.js │ ├── email.js │ ├── integration.js │ ├── invite.js │ ├── label.js │ ├── member-subscription.js │ ├── member-tier.js │ ├── member.js │ ├── navigation-item.js │ ├── newsletter.js │ ├── notification.js │ ├── offer.js │ ├── page.js │ ├── post.js │ ├── role.js │ ├── setting.js │ ├── snippet.js │ ├── tag.js │ ├── theme.js │ ├── tier-benefit-item.js │ ├── tier.js │ ├── user.js │ └── webhook.js ├── modifiers │ ├── autofocus.js │ ├── movable.js │ ├── on-resize.js │ ├── on-scroll.js │ ├── ratio-zoom.js │ ├── react-render.js │ ├── scroll-into-view.js │ ├── scroll-to.js │ └── scroll-top.js ├── router.js ├── routes │ ├── admin.js │ ├── application.js │ ├── authenticated.js │ ├── dashboard.js │ ├── designsandbox.js │ ├── editor.js │ ├── editor │ │ ├── edit.js │ │ ├── index.js │ │ └── new.js │ ├── error404.js │ ├── explore.js │ ├── home.js │ ├── launch.js │ ├── member.js │ ├── member │ │ └── new.js │ ├── members-activity.js │ ├── members.js │ ├── members │ │ └── import.js │ ├── offer.js │ ├── offer │ │ └── new.js │ ├── offers.js │ ├── pages.js │ ├── posts.js │ ├── pro.js │ ├── react-editor.js │ ├── react-editor │ │ ├── edit.js │ │ ├── index.js │ │ └── new.js │ ├── reset.js │ ├── settings.js │ ├── settings │ │ ├── code-injection.js │ │ ├── design.js │ │ ├── design │ │ │ ├── change-theme.js │ │ │ ├── change-theme │ │ │ │ ├── install.js │ │ │ │ └── view.js │ │ │ └── index.js │ │ ├── general.js │ │ ├── integration.js │ │ ├── integration │ │ │ └── webhooks │ │ │ │ ├── edit.js │ │ │ │ └── new.js │ │ ├── integrations.js │ │ ├── integrations │ │ │ ├── amp.js │ │ │ ├── firstpromoter.js │ │ │ ├── new.js │ │ │ ├── slack.js │ │ │ ├── unsplash.js │ │ │ └── zapier.js │ │ ├── labs.js │ │ ├── members-email.js │ │ ├── membership.js │ │ ├── navigation.js │ │ ├── newsletters.js │ │ ├── newsletters │ │ │ ├── edit-newsletter.js │ │ │ └── new-newsletter.js │ │ ├── staff │ │ │ ├── index.js │ │ │ └── user.js │ │ ├── theme-install.js │ │ └── tier │ │ │ └── new.js │ ├── setup.js │ ├── setup │ │ ├── done.js │ │ └── index.js │ ├── signin.js │ ├── signout.js │ ├── signup.js │ ├── site.js │ ├── tag.js │ ├── tag │ │ └── new.js │ ├── tags.js │ ├── unauthenticated.js │ └── whatsnew.js ├── serializers │ ├── action.js │ ├── api-key.js │ ├── application.js │ ├── custom-theme-setting-list.js │ ├── email.js │ ├── integration.js │ ├── invite.js │ ├── label.js │ ├── member.js │ ├── newsletter.js │ ├── notification.js │ ├── page.js │ ├── post.js │ ├── role.js │ ├── setting.js │ ├── tag.js │ ├── theme.js │ ├── tier.js │ ├── user.js │ └── webhook.js ├── services │ ├── ajax.js │ ├── billing.js │ ├── clock.js │ ├── config.js │ ├── custom-theme-settings.js │ ├── custom-views.js │ ├── dashboard-mocks.js │ ├── dashboard-stats.js │ ├── data-cache.js │ ├── dropdown.js │ ├── event-bus.js │ ├── feature.js │ ├── frontend.js │ ├── ghost-paths.js │ ├── lazy-loader.js │ ├── limit.js │ ├── media-queries.js │ ├── media.js │ ├── member-import-validator.js │ ├── members-count-cache.js │ ├── members-stats.js │ ├── members-utils.js │ ├── modals.js │ ├── navigation.js │ ├── notifications.js │ ├── resize-detector.js │ ├── session.js │ ├── settings.js │ ├── slug-generator.js │ ├── tenor.js │ ├── theme-management.js │ ├── ui.js │ ├── unsplash.js │ ├── upgrade-status.js │ ├── utils.js │ └── whats-new.js ├── session-stores │ └── application.js ├── styles │ ├── app-dark.css │ ├── app.css │ ├── components │ │ ├── badges.css │ │ ├── browser-preview.css │ │ ├── codemirror.css │ │ ├── dropdowns.css │ │ ├── filter-builder.css │ │ ├── koenig-dark.css │ │ ├── koenig.css │ │ ├── lists.css │ │ ├── loading-indicator.css │ │ ├── modals-new.css │ │ ├── modals.css │ │ ├── notifications.css │ │ ├── pagination.css │ │ ├── popovers.css │ │ ├── power-calendar.css │ │ ├── power-select.css │ │ ├── publishmenu.css │ │ ├── settings-menu.css │ │ ├── splitbuttons.css │ │ ├── stacks.css │ │ ├── tabs.css │ │ ├── unsplash.css │ │ └── uploader.css │ ├── layouts │ │ ├── apps.css │ │ ├── auth.css │ │ ├── billing.css │ │ ├── content.css │ │ ├── dashboard.css │ │ ├── editor.css │ │ ├── error.css │ │ ├── explore.css │ │ ├── flow.css │ │ ├── fullscreen-wizard.css │ │ ├── labs.css │ │ ├── main.css │ │ ├── member-activity.css │ │ ├── members.css │ │ ├── offers.css │ │ ├── packages.css │ │ ├── portal-settings.css │ │ ├── post-preview.css │ │ ├── preview-email.css │ │ ├── settings.css │ │ ├── tags.css │ │ ├── tiers.css │ │ ├── user.css │ │ ├── users.css │ │ └── whatsnew.css │ ├── patterns │ │ ├── boxes.css │ │ ├── buttons.css │ │ ├── forms.css │ │ ├── global.css │ │ ├── icons.css │ │ ├── labels.css │ │ ├── navlist.css │ │ └── tables.css │ └── spirit │ │ ├── _animations.css │ │ ├── _aspect-ratios.css │ │ ├── _background-position.css │ │ ├── _background-size.css │ │ ├── _border-colors.css │ │ ├── _border-radius.css │ │ ├── _border-style.css │ │ ├── _border-widths.css │ │ ├── _borders.css │ │ ├── _box-shadow.css │ │ ├── _box-sizing.css │ │ ├── _clears.css │ │ ├── _code.css │ │ ├── _colors-dark.css │ │ ├── _colors.css │ │ ├── _coordinates.css │ │ ├── _custom-styles-dark.css │ │ ├── _custom-styles.css │ │ ├── _debug-children.css │ │ ├── _debug-grid.css │ │ ├── _debug.css │ │ ├── _display.css │ │ ├── _dropdown.css │ │ ├── _flexbox.css │ │ ├── _floats.css │ │ ├── _font-family.css │ │ ├── _font-style.css │ │ ├── _font-weight.css │ │ ├── _forms.css │ │ ├── _gradients.css │ │ ├── _heights.css │ │ ├── _hovers.css │ │ ├── _icons.css │ │ ├── _images.css │ │ ├── _letter-spacing.css │ │ ├── _line-height.css │ │ ├── _links.css │ │ ├── _lists.css │ │ ├── _max-widths.css │ │ ├── _media-queries.css │ │ ├── _min-heights.css │ │ ├── _min-widths.css │ │ ├── _module-template.css │ │ ├── _negative-margins.css │ │ ├── _nested.css │ │ ├── _normalize.css │ │ ├── _nudge.css │ │ ├── _opacity.css │ │ ├── _outlines.css │ │ ├── _overflow.css │ │ ├── _pointer-events.css │ │ ├── _position.css │ │ ├── _rotations.css │ │ ├── _skins.css │ │ ├── _spacing.css │ │ ├── _tables.css │ │ ├── _text-align.css │ │ ├── _text-block-spacings.css │ │ ├── _text-decoration.css │ │ ├── _text-transform.css │ │ ├── _type-scale.css │ │ ├── _typography.css │ │ ├── _utilities.css │ │ ├── _vertical-align.css │ │ ├── _visibility.css │ │ ├── _white-space.css │ │ ├── _widths.css │ │ ├── _word-break.css │ │ ├── _z-index.css │ │ ├── spirit-dark.css │ │ └── spirit.css ├── templates │ ├── application-error.hbs │ ├── application.hbs │ ├── dashboard.hbs │ ├── designsandbox.hbs │ ├── editor.hbs │ ├── editor │ │ └── edit-loading.hbs │ ├── error.hbs │ ├── explore.hbs │ ├── launch.hbs │ ├── member.hbs │ ├── members-activity.hbs │ ├── members.hbs │ ├── members │ │ └── import.hbs │ ├── offer.hbs │ ├── offers.hbs │ ├── pages-loading.hbs │ ├── pages.hbs │ ├── posts-loading.hbs │ ├── posts.hbs │ ├── react-editor.hbs │ ├── react-editor │ │ └── edit-loading.hbs │ ├── reset.hbs │ ├── settings.hbs │ ├── settings │ │ ├── code-injection-loading.hbs │ │ ├── code-injection.hbs │ │ ├── design │ │ │ ├── change-theme.hbs │ │ │ └── index.hbs │ │ ├── general-loading.hbs │ │ ├── general.hbs │ │ ├── integration.hbs │ │ ├── integration │ │ │ └── webhooks │ │ │ │ ├── edit.hbs │ │ │ │ └── new.hbs │ │ ├── integrations.hbs │ │ ├── integrations │ │ │ ├── amp-loading.hbs │ │ │ ├── amp.hbs │ │ │ ├── firstpromoter.hbs │ │ │ ├── slack-loading.hbs │ │ │ ├── slack.hbs │ │ │ ├── unsplash-loading.hbs │ │ │ ├── unsplash.hbs │ │ │ └── zapier.hbs │ │ ├── labs-loading.hbs │ │ ├── labs.hbs │ │ ├── membership.hbs │ │ ├── navigation.hbs │ │ ├── newsletters.hbs │ │ └── staff │ │ │ ├── index.hbs │ │ │ ├── user-loading.hbs │ │ │ └── user.hbs │ ├── setup.hbs │ ├── setup │ │ └── done.hbs │ ├── signin.hbs │ ├── signup.hbs │ ├── site.hbs │ ├── tag.hbs │ ├── tags-loading.hbs │ ├── tags.hbs │ └── whatsnew.hbs ├── transforms │ ├── facebook-url-user.js │ ├── json-string.js │ ├── member-subscription.js │ ├── member-tier.js │ ├── members-segment-string.js │ ├── moment-date.js │ ├── moment-utc.js │ ├── navigation-settings.js │ ├── raw.js │ ├── tier-benefits.js │ ├── twitter-url-user.js │ └── visibility-string.js ├── transitions.js ├── transitions │ └── wormhole.js ├── utils │ ├── bound-one-way.js │ ├── caja-sanitizers.js │ ├── copy-text-to-clipboard.js │ ├── ctrl-or-cmd.js │ ├── currency.js │ ├── flatten-grouped-options.js │ ├── format-markdown.js │ ├── get-scroll-parent.js │ ├── ghost-paths.js │ ├── isNumber.js │ ├── link-component.js │ ├── password-generator.js │ ├── publish-options.js │ ├── route.js │ ├── shortcuts.js │ ├── slug-url.js │ └── window-proxy.js └── validators │ ├── base.js │ ├── custom-view.js │ ├── integration.js │ ├── invite-user.js │ ├── label.js │ ├── member.js │ ├── mixins │ └── password.js │ ├── nav-item.js │ ├── new-user.js │ ├── newsletter.js │ ├── offer.js │ ├── post.js │ ├── reset.js │ ├── setting.js │ ├── setup.js │ ├── signin.js │ ├── signup.js │ ├── snippet.js │ ├── subscriber.js │ ├── tag-settings.js │ ├── tier-benefit-item.js │ ├── tier.js │ ├── user.js │ └── webhook.js ├── config ├── coverage.js ├── deprecation-workflow.js ├── environment.js ├── optional-features.json └── targets.js ├── ember-cli-build.js ├── ember-cli-update.json ├── lib ├── asset-delivery │ ├── index.js │ └── package.json └── koenig-editor │ ├── addon │ ├── components │ │ ├── kg-action-bar.hbs │ │ ├── kg-action-bar.js │ │ ├── koenig-alt-input.hbs │ │ ├── koenig-alt-input.js │ │ ├── koenig-basic-html-input.hbs │ │ ├── koenig-basic-html-input.js │ │ ├── koenig-basic-html-textarea.hbs │ │ ├── koenig-basic-html-textarea.js │ │ ├── koenig-caption-input.hbs │ │ ├── koenig-caption-input.js │ │ ├── koenig-card-audio.hbs │ │ ├── koenig-card-audio.js │ │ ├── koenig-card-before-after.hbs │ │ ├── koenig-card-before-after.js │ │ ├── koenig-card-bookmark.hbs │ │ ├── koenig-card-bookmark.js │ │ ├── koenig-card-button.hbs │ │ ├── koenig-card-button.js │ │ ├── koenig-card-callout.hbs │ │ ├── koenig-card-callout.js │ │ ├── koenig-card-code.hbs │ │ ├── koenig-card-code.js │ │ ├── koenig-card-email-cta.hbs │ │ ├── koenig-card-email-cta.js │ │ ├── koenig-card-email.hbs │ │ ├── koenig-card-email.js │ │ ├── koenig-card-embed.hbs │ │ ├── koenig-card-embed.js │ │ ├── koenig-card-embed │ │ │ ├── nft.hbs │ │ │ └── nft.js │ │ ├── koenig-card-file.hbs │ │ ├── koenig-card-file.js │ │ ├── koenig-card-gallery.hbs │ │ ├── koenig-card-gallery.js │ │ ├── koenig-card-header.hbs │ │ ├── koenig-card-header.js │ │ ├── koenig-card-hr.hbs │ │ ├── koenig-card-hr.js │ │ ├── koenig-card-html.hbs │ │ ├── koenig-card-html.js │ │ ├── koenig-card-image.hbs │ │ ├── koenig-card-image.js │ │ ├── koenig-card-image │ │ │ ├── selector-tenor.hbs │ │ │ ├── selector-tenor.js │ │ │ └── selector-tenor │ │ │ │ ├── gif.hbs │ │ │ │ └── gif.js │ │ ├── koenig-card-markdown.hbs │ │ ├── koenig-card-markdown.js │ │ ├── koenig-card-paywall.hbs │ │ ├── koenig-card-paywall.js │ │ ├── koenig-card-product.hbs │ │ ├── koenig-card-product.js │ │ ├── koenig-card-toggle.hbs │ │ ├── koenig-card-toggle.js │ │ ├── koenig-card-video.hbs │ │ ├── koenig-card-video.js │ │ ├── koenig-card.hbs │ │ ├── koenig-card.js │ │ ├── koenig-editor.hbs │ │ ├── koenig-editor.js │ │ ├── koenig-link-input.hbs │ │ ├── koenig-link-input.js │ │ ├── koenig-link-toolbar.hbs │ │ ├── koenig-link-toolbar.js │ │ ├── koenig-media-selector.hbs │ │ ├── koenig-media-selector.js │ │ ├── koenig-menu-content.hbs │ │ ├── koenig-menu-content.js │ │ ├── koenig-plus-menu.hbs │ │ ├── koenig-plus-menu.js │ │ ├── koenig-settings-panel.hbs │ │ ├── koenig-settings-panel.js │ │ ├── koenig-slash-menu.hbs │ │ ├── koenig-slash-menu.js │ │ ├── koenig-snippet-input.hbs │ │ ├── koenig-snippet-input.js │ │ ├── koenig-text-replacement-html-input.hbs │ │ ├── koenig-text-replacement-html-input.js │ │ ├── koenig-toolbar.hbs │ │ └── koenig-toolbar.js │ ├── helpers │ │ ├── card-is-available.js │ │ ├── clean-basic-html.js │ │ ├── kg-style.js │ │ └── sanitize-html.js │ ├── lib │ │ ├── clean-text-replacement-html.js │ │ ├── dnd │ │ │ ├── constants.js │ │ │ ├── container.js │ │ │ ├── scroll-handler.js │ │ │ └── utils.js │ │ └── relative-to-absolute.js │ ├── options │ │ ├── atoms.js │ │ ├── basic-html-parser-plugins.js │ │ ├── cards.js │ │ ├── key-commands.js │ │ └── text-expansions.js │ ├── services │ │ ├── koenig-drag-drop-handler.js │ │ └── koenig-ui.js │ └── utils │ │ ├── create-component-atom.js │ │ ├── create-component-card.js │ │ ├── extract-audio-metadata.js │ │ ├── extract-video-metadata.js │ │ ├── get-scroll-parent.js │ │ ├── insert-cards-from-files.js │ │ ├── localstorage.js │ │ ├── markup-utils.js │ │ ├── oembed.js │ │ ├── prettify-file-name.js │ │ ├── reading-time.js │ │ └── snippet-icon.js │ ├── app │ ├── components │ │ ├── kg-action-bar.js │ │ ├── koenig-alt-input.js │ │ ├── koenig-basic-html-input.js │ │ ├── koenig-basic-html-textarea.js │ │ ├── koenig-caption-input.js │ │ ├── koenig-card-audio.js │ │ ├── koenig-card-before-after.js │ │ ├── koenig-card-bookmark.js │ │ ├── koenig-card-button.js │ │ ├── koenig-card-callout.js │ │ ├── koenig-card-code.js │ │ ├── koenig-card-email-cta.js │ │ ├── koenig-card-email.js │ │ ├── koenig-card-embed.js │ │ ├── koenig-card-embed │ │ │ └── nft.js │ │ ├── koenig-card-file.js │ │ ├── koenig-card-gallery.js │ │ ├── koenig-card-header.js │ │ ├── koenig-card-hr.js │ │ ├── koenig-card-html.js │ │ ├── koenig-card-image.js │ │ ├── koenig-card-image │ │ │ ├── selector-tenor.js │ │ │ └── selector-tenor │ │ │ │ └── gif.js │ │ ├── koenig-card-markdown.js │ │ ├── koenig-card-paywall.js │ │ ├── koenig-card-product.js │ │ ├── koenig-card-toggle.js │ │ ├── koenig-card-video.js │ │ ├── koenig-card.js │ │ ├── koenig-editor.js │ │ ├── koenig-link-input.js │ │ ├── koenig-link-toolbar.js │ │ ├── koenig-media-selector.js │ │ ├── koenig-menu-content.js │ │ ├── koenig-plus-menu.js │ │ ├── koenig-settings-panel.js │ │ ├── koenig-slash-menu.js │ │ ├── koenig-snippet-input.js │ │ ├── koenig-text-replacement-html-input.js │ │ └── koenig-toolbar.js │ ├── helpers │ │ ├── card-is-available.js │ │ ├── clean-basic-html.js │ │ ├── kg-style.js │ │ └── sanitize-html.js │ └── services │ │ ├── koenig-drag-drop-handler.js │ │ └── koenig-ui.js │ ├── docs │ └── specs │ │ └── popup-toolbar.md │ ├── index.js │ ├── package.json │ └── public │ └── icons │ └── koenig │ ├── card-indicator-email.svg │ ├── card-indicator-html.svg │ ├── card-indicator-markdown.svg │ ├── code-block.svg │ ├── kg-add.svg │ ├── kg-bold.svg │ ├── kg-card-type-audio.svg │ ├── kg-card-type-before-after.svg │ ├── kg-card-type-bookmark.svg │ ├── kg-card-type-button.svg │ ├── kg-card-type-callout.svg │ ├── kg-card-type-codepen.svg │ ├── kg-card-type-divider.svg │ ├── kg-card-type-email-cta.svg │ ├── kg-card-type-email.svg │ ├── kg-card-type-facebook.svg │ ├── kg-card-type-file.svg │ ├── kg-card-type-gallery.svg │ ├── kg-card-type-gen-embed.svg │ ├── kg-card-type-gif.svg │ ├── kg-card-type-header.svg │ ├── kg-card-type-html.svg │ ├── kg-card-type-image.svg │ ├── kg-card-type-instagram.svg │ ├── kg-card-type-markdown.svg │ ├── kg-card-type-nft.svg │ ├── kg-card-type-other.svg │ ├── kg-card-type-paywall.svg │ ├── kg-card-type-product.svg │ ├── kg-card-type-snippet-block.svg │ ├── kg-card-type-snippet-combination.svg │ ├── kg-card-type-snippet-text.svg │ ├── kg-card-type-soundcloud.svg │ ├── kg-card-type-spotify.svg │ ├── kg-card-type-toggle.svg │ ├── kg-card-type-twitter.svg │ ├── kg-card-type-unsplash.svg │ ├── kg-card-type-video.svg │ ├── kg-card-type-vimeo.svg │ ├── kg-card-type-youtube.svg │ ├── kg-cta-border.svg │ ├── kg-edit.svg │ ├── kg-header-full-center.svg │ ├── kg-header-full-left.svg │ ├── kg-header-full-right.svg │ ├── kg-header-wide-center.svg │ ├── kg-header-wide-left.svg │ ├── kg-header-wide-right.svg │ ├── kg-heading-1.svg │ ├── kg-heading-2.svg │ ├── kg-img-full.svg │ ├── kg-img-regular.svg │ ├── kg-img-wide.svg │ ├── kg-italic.svg │ ├── kg-link.svg │ ├── kg-quote-1.svg │ ├── kg-quote-2.svg │ ├── kg-quote.svg │ ├── kg-replace.svg │ ├── kg-snippet.svg │ ├── kg-star.svg │ ├── kg-thin-delete.svg │ ├── kg-thin-edit.svg │ ├── kg-toggle-card-open-arrow.svg │ └── kg-trash.svg ├── mirage ├── .eslintrc.js ├── config.js ├── config │ ├── api-keys.js │ ├── authentication.js │ ├── config.js │ ├── custom-theme-settings.js │ ├── emails.js │ ├── integrations.js │ ├── invites.js │ ├── labels.js │ ├── members.js │ ├── newsletters.js │ ├── offers.js │ ├── pages.js │ ├── posts.js │ ├── roles.js │ ├── settings.js │ ├── site.js │ ├── slugs.js │ ├── snippets.js │ ├── stats.js │ ├── tags.js │ ├── themes.js │ ├── tiers.js │ ├── uploads.js │ ├── users.js │ └── webhooks.js ├── factories │ ├── api-key.js │ ├── email.js │ ├── integration.js │ ├── invite.js │ ├── label.js │ ├── member-activity-event.js │ ├── member.js │ ├── notification.js │ ├── offer.js │ ├── post.js │ ├── role.js │ ├── subscription.js │ ├── tag.js │ ├── tier.js │ ├── user.js │ └── webhook.js ├── fixtures │ ├── configs.js │ ├── newsletters.js │ ├── roles.js │ ├── settings.js │ ├── sites.js │ ├── themes.js │ ├── tiers.js │ └── timezones.js ├── models │ ├── api-key.js │ ├── config.js │ ├── custom-theme-setting.js │ ├── email.js │ ├── integration.js │ ├── invite.js │ ├── label.js │ ├── member-activity-event.js │ ├── member.js │ ├── newsletter.js │ ├── notification.js │ ├── page.js │ ├── post.js │ ├── role.js │ ├── site.js │ ├── snippet.js │ ├── subscriber.js │ ├── subscription.js │ ├── tag.js │ ├── theme.js │ ├── tier.js │ ├── user.js │ └── webhook.js ├── routes-dev.js ├── routes-test.js ├── scenarios │ └── default.js ├── serializers │ ├── application.js │ ├── integration.js │ ├── label.js │ ├── member-activity-event.js │ ├── member.js │ ├── page.js │ ├── post.js │ ├── subscription.js │ ├── tag.js │ ├── tier.js │ └── user.js └── utils.js ├── package.json ├── public └── assets │ ├── fonts │ └── Inter.ttf │ ├── icons │ ├── account-group.svg │ ├── activity-placeholder.svg │ ├── add-stroke.svg │ ├── add-view.svg │ ├── add.svg │ ├── align-center.svg │ ├── align-left.svg │ ├── ambulance.svg │ ├── analytics.svg │ ├── arrow-down-small.svg │ ├── arrow-down-stroke.svg │ ├── arrow-down.svg │ ├── arrow-left-small.svg │ ├── arrow-left-stroke.svg │ ├── arrow-left-tail.svg │ ├── arrow-left.svg │ ├── arrow-right-small.svg │ ├── arrow-right-stroke.svg │ ├── arrow-right-tail.svg │ ├── arrow-right.svg │ ├── arrow-up-small.svg │ ├── arrow-up-stroke.svg │ ├── arrow-up.svg │ ├── arrow2-down.svg │ ├── arrow2-right.svg │ ├── audio-file.svg │ ├── audio-upload.svg │ ├── book-open.svg │ ├── bookmark-article.svg │ ├── box-hands.svg │ ├── box.svg │ ├── boxes.svg │ ├── brackets.svg │ ├── button.svg │ ├── calendar.svg │ ├── cash.svg │ ├── chat-double-bubble.svg │ ├── check-2.svg │ ├── check-circle-stroke.svg │ ├── check-circle.svg │ ├── check.svg │ ├── cheeseburger.svg │ ├── circle-ellipsis.svg │ ├── clock.svg │ ├── clockface.svg │ ├── close-stroke.svg │ ├── close.svg │ ├── cloud.svg │ ├── compass-2.svg │ ├── compass.svg │ ├── computer.svg │ ├── confetti.svg │ ├── content-bold.svg │ ├── content.svg │ ├── copy.svg │ ├── credit-card.svg │ ├── cross-circle.svg │ ├── cycle.svg │ ├── default-favicon.svg │ ├── desert.svg │ ├── desktop.svg │ ├── diamond.svg │ ├── discount-bubble.svg │ ├── dividers.svg │ ├── dotdotdot.svg │ ├── download-circle.svg │ ├── download.svg │ ├── eco-globe.svg │ ├── eco-lightbulb.svg │ ├── edit-view.svg │ ├── ellipsis.svg │ ├── email-at.svg │ ├── email-body.svg │ ├── email-footer.svg │ ├── email-header.svg │ ├── email-love-letter.svg │ ├── email-member.svg │ ├── email-name.svg │ ├── email-send.svg │ ├── email-stroke.svg │ ├── email-unread.svg │ ├── email.svg │ ├── event-canceled-subscription.svg │ ├── event-comment.svg │ ├── event-email-delivery-failed.svg │ ├── event-logged-in.svg │ ├── event-made-a-payment.svg │ ├── event-opened-email.svg │ ├── event-received-email.svg │ ├── event-signed-up.svg │ ├── event-subscribed-to-email.svg │ ├── event-subscriptions.svg │ ├── event-unsubscribed-from-email.svg │ ├── expand.svg │ ├── external.svg │ ├── eye.svg │ ├── facebook-heart.svg │ ├── facebook-like.svg │ ├── facebook.svg │ ├── feature-image.svg │ ├── file-download.svg │ ├── file-tabular-data.svg │ ├── file-text-document.svg │ ├── file-upload.svg │ ├── film-camera.svg │ ├── filter.svg │ ├── firstpromoter.png │ ├── folder.svg │ ├── gallery-placeholder.svg │ ├── get-started-import.svg │ ├── get-started-members.svg │ ├── get-started-migrations.svg │ ├── get-started.svg │ ├── ghost-logo-orb.svg │ ├── ghost-logo.svg │ ├── ghost-orb.svg │ ├── ghost-squircle.svg │ ├── gift.svg │ ├── github-outline.svg │ ├── github-star.svg │ ├── github.svg │ ├── globe.svg │ ├── google-favicon.svg │ ├── google-search.svg │ ├── google.svg │ ├── grab.svg │ ├── graph-line.svg │ ├── heart-beat.svg │ ├── heart-circle.svg │ ├── heart.svg │ ├── help.svg │ ├── hotspot.svg │ ├── house-bold.svg │ ├── house.svg │ ├── icon.svg │ ├── id-card.svg │ ├── idea.svg │ ├── info.svg │ ├── instagram.svg │ ├── integration.svg │ ├── labs.svg │ ├── laptop.svg │ ├── line.svg │ ├── link.svg │ ├── list-bullet.svg │ ├── list-number.svg │ ├── lock.svg │ ├── lock2.svg │ ├── loop-infinite.svg │ ├── lotus.svg │ ├── markdown.svg │ ├── member-add.svg │ ├── member.svg │ ├── members-all.svg │ ├── members-bold.svg │ ├── members-outline.svg │ ├── members-paid.svg │ ├── members-placeholder.svg │ ├── members-post.svg │ ├── members-segment.svg │ ├── members.svg │ ├── mobile-phone-heart.svg │ ├── mobile-phone.svg │ ├── module.svg │ ├── modules.svg │ ├── monitor-labs.svg │ ├── moon.svg │ ├── mountains.svg │ ├── mute.svg │ ├── navigation.svg │ ├── network.svg │ ├── news-article.svg │ ├── nightshift.svg │ ├── no-data-line-chart.svg │ ├── no-data-list.svg │ ├── no-data-subscription.svg │ ├── no-email.svg │ ├── no-members.svg │ ├── offer.svg │ ├── page-bold.svg │ ├── page.svg │ ├── pages-placeholder.svg │ ├── paint-palette.svg │ ├── paintbrush.svg │ ├── pause.svg │ ├── pen.svg │ ├── pencil-circle.svg │ ├── pencil.svg │ ├── percentage.svg │ ├── photos-people.svg │ ├── photos.svg │ ├── piggy-bank.svg │ ├── pin.svg │ ├── plane.svg │ ├── play.svg │ ├── plus.svg │ ├── portal-icon-1.svg │ ├── portal-icon-2.svg │ ├── portal-icon-3.svg │ ├── portal-icon-4.svg │ ├── portal-icon-5.svg │ ├── portal-logo-stroke.svg │ ├── portal-logo.svg │ ├── post.svg │ ├── posts-placeholder.svg │ ├── posts.svg │ ├── powered-by-stripe.svg │ ├── powered-by-tenor.svg │ ├── presentation-code.svg │ ├── published-post.svg │ ├── recycle.svg │ ├── reload.svg │ ├── repo.svg │ ├── retry.svg │ ├── satellite.svg │ ├── search.svg │ ├── send-email.svg │ ├── server.svg │ ├── settings.svg │ ├── shield-lock.svg │ ├── shield.svg │ ├── sidemenu-open.svg │ ├── sidemenu.svg │ ├── signal-tower.svg │ ├── signout.svg │ ├── smiley.svg │ ├── social-facebook.svg │ ├── social-share.svg │ ├── social-twitter.svg │ ├── spinner.svg │ ├── staff.svg │ ├── star-filled.svg │ ├── store.svg │ ├── stripe-verified-partner-badge.svg │ ├── summer.svg │ ├── sun.svg │ ├── support.svg │ ├── sync.svg │ ├── tag.svg │ ├── tags-placeholder.svg │ ├── tenor.svg │ ├── terminal.svg │ ├── text-vector.svg │ ├── text.svg │ ├── theme.svg │ ├── ticket.svg │ ├── trash.svg │ ├── trophy.svg │ ├── tumbleweed.svg │ ├── twitter-comment.svg │ ├── twitter-like.svg │ ├── twitter-link.svg │ ├── twitter-logo.svg │ ├── twitter-retweet.svg │ ├── twitter-share.svg │ ├── twitter.svg │ ├── ufo-attack.svg │ ├── unmute.svg │ ├── unsplash-heart.svg │ ├── unsplash.svg │ ├── unsubscribed.svg │ ├── upload-fill.svg │ ├── upload.svg │ ├── user-circle.svg │ ├── user-group.svg │ ├── user-group2.svg │ ├── v-ellipsis.svg │ ├── view-site.svg │ ├── warning-stroke.svg │ ├── warning.svg │ ├── window-app.svg │ ├── window-pulse.svg │ ├── wrench-double.svg │ └── zap.svg │ └── img │ ├── 404-ghost.png │ ├── 404-ghost@2x.png │ ├── abstract-2.jpg │ ├── abstract.jpg │ ├── amp.svg │ ├── apple-touch-icon.png │ ├── buffer.png │ ├── community-background.jpg │ ├── community.jpg │ ├── dashboard-feature-image.jpeg │ ├── dashboard │ ├── bp1.jpg │ ├── bp2.jpg │ └── join-community.jpg │ ├── disqus.svg │ ├── favicon.ico │ ├── firstpromoter.png │ ├── footer-marketplace-bg.png │ ├── get-started.jpg │ ├── github.svg │ ├── google-analytics.png │ ├── google-docs.svg │ ├── install-welcome.png │ ├── invite-placeholder.png │ ├── large.png │ ├── launch-wizard-bg.png │ ├── loadingcat.gif │ ├── logos │ ├── ghost-logo-black-1.png │ ├── orb-black-1.png │ ├── orb-black-2.png │ ├── orb-black-3.png │ ├── orb-black-4.png │ └── orb-black-5.png │ ├── mailchimp.svg │ ├── marketing │ ├── members-1.jpg │ ├── members-2.jpg │ ├── offers-1.jpg │ ├── offers-2.jpg │ └── offers-3.jpg │ ├── medium.png │ ├── more.png │ ├── newsletter-1.jpg │ ├── newsletter-2.jpg │ ├── orb-squircle.png │ ├── patreon.svg │ ├── paypal.svg │ ├── plausible.png │ ├── resource-1.jpg │ ├── slackicon.png │ ├── small.png │ ├── themes │ ├── Alto-cut.jpg │ ├── Alto.jpg │ ├── Alto.png │ ├── Bulletin.png │ ├── Casper.jpg │ ├── Dawn.png │ ├── Digest.png │ ├── Dope.png │ ├── Ease-cut.jpg │ ├── Ease.jpg │ ├── Ease.png │ ├── Edge.png │ ├── Edition-cut.jpg │ ├── Edition.jpg │ ├── Edition.png │ ├── Headline.jpg │ ├── Journal.png │ ├── London-cut.jpg │ ├── London.jpg │ ├── Ruby.png │ └── Wave.png │ ├── touch-icon-ipad.png │ ├── touch-icon-iphone.png │ ├── twitter.svg │ ├── typeform.svg │ ├── ulysses.png │ ├── unsplash-404.png │ ├── user-cover.png │ ├── user-image.png │ ├── users.png │ ├── whats-new-header-bg.svg │ ├── zapier-logo.svg │ ├── zapier.svg │ └── zero-bounce.png ├── renovate.json ├── testem.js ├── tests ├── .eslintrc.js ├── acceptance │ ├── authentication-test.js │ ├── content-test.js │ ├── custom-post-templates-test.js │ ├── dashboard-test.js │ ├── editor-test.js │ ├── editor │ │ └── publish-flow-test.js │ ├── error-handling-test.js │ ├── launch-flow-test.js │ ├── members-activity-test.js │ ├── members-test.js │ ├── members │ │ ├── details-test.js │ │ ├── filter-test.js │ │ └── import-test.js │ ├── offers-test.js │ ├── password-reset-test.js │ ├── settings │ │ ├── amp-test.js │ │ ├── code-injection-test.js │ │ ├── design-test.js │ │ ├── general-test.js │ │ ├── integrations-test.js │ │ ├── labs-test.js │ │ ├── membership-test.js │ │ ├── navigation-test.js │ │ ├── newsletters-test.js │ │ ├── slack-test.js │ │ ├── tags-test.js │ │ ├── unsplash-test.js │ │ └── zapier-test.js │ ├── setup-test.js │ ├── signin-test.js │ ├── signup-test.js │ └── staff-test.js ├── helpers │ ├── file-upload.js │ ├── labs-flag.js │ ├── login-as-role.js │ ├── mailgun.js │ ├── members.js │ ├── newsletters.js │ ├── stripe.js │ └── visit.js ├── index.html ├── integration │ ├── adapters │ │ ├── tag-test.js │ │ └── user-test.js │ ├── components │ │ ├── gh-alert-test.js │ │ ├── gh-alerts-test.js │ │ ├── gh-basic-dropdown-test.js │ │ ├── gh-cm-editor-test.js │ │ ├── gh-date-picker-test.js │ │ ├── gh-date-time-picker-test.js │ │ ├── gh-distribution-action-select-test.js │ │ ├── gh-feature-flag-test.js │ │ ├── gh-file-uploader-test.js │ │ ├── gh-image-uploader-test.js │ │ ├── gh-image-uploader-with-preview-test.js │ │ ├── gh-member-avatar-test.js │ │ ├── gh-members-import-table-test.js │ │ ├── gh-navitem-test.js │ │ ├── gh-navitem-url-input-test.js │ │ ├── gh-notification-test.js │ │ ├── gh-notifications-test.js │ │ ├── gh-psm-tags-input-test.js │ │ ├── gh-psm-template-select-test.js │ │ ├── gh-psm-visibility-input-test.js │ │ ├── gh-search-input-test.js │ │ ├── gh-tag-settings-form-test.js │ │ ├── gh-task-button-test.js │ │ ├── gh-theme-table-test.js │ │ ├── gh-timezone-select-test.js │ │ ├── gh-trim-focus-input-test.js │ │ ├── gh-unsplash-photo-test.js │ │ ├── gh-unsplash-test.js │ │ ├── gh-uploader-test.js │ │ ├── gh-validation-status-container-test.js │ │ ├── gh-whats-new-test.js │ │ ├── koenig-toolbar-test.js │ │ ├── modal-import-members-test.js │ │ └── modal-transfer-owner-test.js │ ├── helpers │ │ ├── background-image-style-test.js │ │ ├── clean-basic-html-test.js │ │ ├── gh-format-post-time-test.js │ │ ├── gh-url-preview-test.js │ │ └── sanitize-html-test.js │ └── services │ │ ├── ajax-test.js │ │ ├── config-test.js │ │ ├── feature-test.js │ │ ├── lazy-loader-test.js │ │ ├── member-import-validator-test.js │ │ ├── slug-generator-test.js │ │ └── store-test.js ├── test-helper.js └── unit │ ├── .gitkeep │ ├── authenticators │ └── cookie-test.js │ ├── components │ └── gh-post-settings-menu-test.js │ ├── controllers │ ├── editor-test.js │ └── settings │ │ └── design-test.js │ ├── helpers │ ├── gh-count-characters-test.js │ ├── gh-count-down-characters-test.js │ ├── gh-user-can-admin-test.js │ ├── highlighted-text-test.js │ └── most-recently-updated-test.js │ ├── mixins │ └── validation-engine-test.js │ ├── models │ ├── invite-test.js │ ├── member-test.js │ ├── navigation-item-test.js │ ├── post-test.js │ ├── role-test.js │ ├── setting-test.js │ ├── tag-test.js │ └── user-test.js │ ├── routes │ └── explore-test.js │ ├── serializers │ └── notification-test.js │ ├── services │ ├── event-bus-test.js │ ├── limit-test.js │ ├── member-stats-test.js │ ├── notifications-test.js │ └── unsplash-test.js │ ├── transforms │ ├── facebook-url-user-test.js │ ├── json-string-test.js │ ├── navigation-settings-test.js │ └── twitter-url-user-test.js │ ├── utils │ └── ghost-paths-test.js │ └── validators │ ├── nav-item-test.js │ ├── post-test.js │ └── tag-settings-test.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 4 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.hbs] 14 | insert_final_newline = false 15 | 16 | [{package,bower}.json] 17 | indent_size = 2 18 | 19 | [*.{diff,md}] 20 | trim_trailing_whitespace = false 21 | 22 | [*.yml] 23 | indent_size = 2 24 | 25 | [Makefile] 26 | indent_style = tab 27 | -------------------------------------------------------------------------------- /.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | "component-structure": "flat", 3 | "component-class": "@glimmer/component", 4 | /** 5 | Ember CLI sends analytics information by default. The data is completely 6 | anonymous, but there are times when you might want to disable this behavior. 7 | 8 | Setting `disableAnalytics` to true will prevent any data from being sent. 9 | */ 10 | "disableAnalytics": true 11 | } 12 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | /vendor/ 4 | 5 | # compiled output 6 | /dist/ 7 | /tmp/ 8 | 9 | # dependencies 10 | /bower_components/ 11 | /node_modules/ 12 | 13 | # misc 14 | /coverage/ 15 | .eslintcache 16 | 17 | # ember-try 18 | /.node_modules.ember-try/ 19 | /bower.json.ember-try 20 | /package.json.ember-try 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Got some code for us? Awesome 🎊! 2 | 3 | Please include a description of your change & check your PR against this list, thanks! 4 | 5 | - [ ] There's a clear use-case for this code change 6 | - [ ] Commit message has a short title & references relevant issues 7 | - [ ] The build will pass (run `ember test` from the repo root - will be `core/admin` if working from the submodule in Ghost). 8 | 9 | More info can be found by clicking the "guidelines for contributing" link above. 10 | -------------------------------------------------------------------------------- /.lint-todorc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'ember-template-lint': { 3 | daysToDecay: { 4 | warn: 120, 5 | error: 180, 6 | } 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /.template-lintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: "recommended", 3 | 4 | rules: { 5 | 'no-forbidden-elements': ['meta', 'html', 'script'], 6 | 'no-implicit-this': {allow: ['noop', 'now', 'site-icon-style', 'accent-color-background']}, 7 | 'no-inline-styles': false 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /app/adapters/application.js: -------------------------------------------------------------------------------- 1 | import EmbeddedRelationAdapter from 'ghost-admin/adapters/embedded-relation-adapter'; 2 | import classic from 'ember-classic-decorator'; 3 | 4 | @classic 5 | export default class Application extends EmbeddedRelationAdapter { 6 | shouldBackgroundReloadRecord() { 7 | return false; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /app/adapters/email.js: -------------------------------------------------------------------------------- 1 | import ApplicationAdapter from './application'; 2 | import classic from 'ember-classic-decorator'; 3 | 4 | @classic 5 | export default class Email extends ApplicationAdapter { 6 | retry(model) { 7 | let url = `${this.buildURL('email', model.get('id'))}retry/`; 8 | 9 | return this.ajax(url, 'PUT', {data: {}}).then((data) => { 10 | this.store.pushPayload(data); 11 | return model; 12 | }); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/adapters/label.js: -------------------------------------------------------------------------------- 1 | import ApplicationAdapter from 'ghost-admin/adapters/application'; 2 | import SlugUrl from 'ghost-admin/utils/slug-url'; 3 | import classic from 'ember-classic-decorator'; 4 | 5 | @classic 6 | export default class Label extends ApplicationAdapter { 7 | buildURL(_modelName, _id, _snapshot, _requestType, query) { 8 | let url = super.buildURL(...arguments); 9 | 10 | return SlugUrl(url, query); 11 | } 12 | } -------------------------------------------------------------------------------- /app/adapters/page.js: -------------------------------------------------------------------------------- 1 | import ApplicationAdapter from 'ghost-admin/adapters/application'; 2 | import classic from 'ember-classic-decorator'; 3 | 4 | @classic 5 | export default class Page extends ApplicationAdapter { 6 | // posts and pages now include everything by default 7 | buildIncludeURL(store, modelName, id, snapshot, requestType, query) { 8 | return this.buildURL(modelName, id, snapshot, requestType, query); 9 | } 10 | 11 | buildQuery(store, modelName, options) { 12 | return options; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/adapters/tag.js: -------------------------------------------------------------------------------- 1 | import ApplicationAdapter from 'ghost-admin/adapters/application'; 2 | import SlugUrl from 'ghost-admin/utils/slug-url'; 3 | import classic from 'ember-classic-decorator'; 4 | 5 | @classic 6 | export default class Tag extends ApplicationAdapter { 7 | buildURL(_modelName, _id, _snapshot, _requestType, query) { 8 | let url = super.buildURL(...arguments); 9 | 10 | return SlugUrl(url, query); 11 | } 12 | } -------------------------------------------------------------------------------- /app/adapters/theme.js: -------------------------------------------------------------------------------- 1 | import ApplicationAdapter from './application'; 2 | import classic from 'ember-classic-decorator'; 3 | 4 | @classic 5 | export default class Theme extends ApplicationAdapter { 6 | activate(model) { 7 | let url = `${this.buildURL('theme', model.get('id'))}activate/`; 8 | 9 | return this.ajax(url, 'PUT', {data: {}}).then((data) => { 10 | this.store.pushPayload(data); 11 | return model; 12 | }); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/components/aspect-ratio-box.hbs: -------------------------------------------------------------------------------- 1 | {{#unless this.isResizing}} 2 | {{yield}} 3 | {{/unless}} -------------------------------------------------------------------------------- /app/components/dashboard/parts/percentage.hbs: -------------------------------------------------------------------------------- 1 | {{#if (gt @percentage 0) }} 2 |
+{{ @percentage }}%
3 | {{else if (lt @percentage 0)}} 4 |
{{ @percentage }}%
5 | {{else}} 6 |
0%
7 | {{/if}} -------------------------------------------------------------------------------- /app/components/dashboard/parts/zero.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Welcome to your Dashboard

4 |

You'll find member analytics here once
someone signs up.

5 |

Add or import members →

6 |
7 |
8 | -------------------------------------------------------------------------------- /app/components/editor/modals/preview/browser.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import copyTextToClipboard from 'ghost-admin/utils/copy-text-to-clipboard'; 3 | import {task, timeout} from 'ember-concurrency'; 4 | 5 | export default class ModalPostPreviewBrowserComponent extends Component { 6 | @task 7 | *copyPreviewUrl() { 8 | copyTextToClipboard(this.args.post.previewUrl); 9 | yield timeout(this.isTesting ? 50 : 3000); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/components/editor/modals/preview/mobile.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
6 |
7 |
-------------------------------------------------------------------------------- /app/components/editor/modals/preview/mobile.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import copyTextToClipboard from 'ghost-admin/utils/copy-text-to-clipboard'; 3 | import {task, timeout} from 'ember-concurrency'; 4 | 5 | export default class ModalPostPreviewBrowserComponent extends Component { 6 | @task 7 | *copyPreviewUrl() { 8 | copyTextToClipboard(this.args.post.previewUrl); 9 | yield timeout(this.isTesting ? 50 : 3000); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/components/editor/modals/publish-flow/options.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import {action} from '@ember/object'; 3 | import {tracked} from '@glimmer/tracking'; 4 | 5 | export default class PublishFlowOptions extends Component { 6 | @tracked openSection = null; 7 | 8 | @action 9 | toggleSection(section) { 10 | if (section === this.openSection) { 11 | this.openSection = null; 12 | } else { 13 | this.openSection = section; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/components/editor/publish-management.hbs: -------------------------------------------------------------------------------- 1 | {{yield (hash 2 | post=@post 3 | publishOptions=this.publishOptions 4 | openPreview=this.openPreview 5 | togglePreview=this.togglePreview 6 | saveTask=this.saveTask 7 | saveButtonTaskGroup=this.saveButtonTaskGroup 8 | hasUnsavedChanges=@hasUnsavedChanges 9 | openPublishFlow=this.openPublishFlow 10 | openUpdateFlow=this.openUpdateFlow 11 | )}} 12 | -------------------------------------------------------------------------------- /app/components/editor/publish-options/publish-type.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import {action} from '@ember/object'; 3 | 4 | export default class PublishTypeOption extends Component { 5 | @action 6 | onChange(event) { 7 | event.preventDefault(); 8 | this.args.publishOptions.setPublishType(event.target.value); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/components/epm-modal-container.js: -------------------------------------------------------------------------------- 1 | import EpmModalContainer from '@tryghost/ember-promise-modals/components/modal-container'; 2 | 3 | export default class extends EpmModalContainer {} 4 | -------------------------------------------------------------------------------- /app/components/gh-alert.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{@message.message}} 4 |
5 | 8 |
-------------------------------------------------------------------------------- /app/components/gh-alerts.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/components/gh-alerts.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import {inject as service} from '@ember/service'; 3 | 4 | export default class GhAlerts extends Component { 5 | @service notifications; 6 | } 7 | -------------------------------------------------------------------------------- /app/components/gh-app.hbs: -------------------------------------------------------------------------------- 1 |
2 | {{yield}} 3 |
4 | -------------------------------------------------------------------------------- /app/components/gh-basic-dropdown.js: -------------------------------------------------------------------------------- 1 | import BasicDropdown from 'ember-basic-dropdown/components/basic-dropdown'; 2 | import {inject as service} from '@ember/service'; 3 | 4 | class GhBasicDropdown extends BasicDropdown { 5 | @service dropdown; 6 | 7 | constructor() { 8 | super(...arguments); 9 | this.dropdown.on('close', this, this.close); 10 | } 11 | 12 | willDestroy() { 13 | this.dropdown.off('close', this, this.close); 14 | } 15 | } 16 | 17 | export default GhBasicDropdown; 18 | -------------------------------------------------------------------------------- /app/components/gh-billing-iframe.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/components/gh-billing-modal.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
-------------------------------------------------------------------------------- /app/components/gh-billing-modal.js: -------------------------------------------------------------------------------- 1 | import Component from '@ember/component'; 2 | import classic from 'ember-classic-decorator'; 3 | import {computed} from '@ember/object'; 4 | import {inject as service} from '@ember/service'; 5 | 6 | @classic 7 | export default class GhBillingModal extends Component { 8 | @service billing; 9 | 10 | @computed('billingWindowOpen') 11 | get visibilityClass() { 12 | return this.billingWindowOpen ? 'gh-billing' : 'gh-billing closed'; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/components/gh-billing-update-button.hbs: -------------------------------------------------------------------------------- 1 | {{#if this.showUpgradeButton}} 2 | 3 | {{/if}} 4 | -------------------------------------------------------------------------------- /app/components/gh-blog-url.hbs: -------------------------------------------------------------------------------- 1 | {{{this.config.blogUrl}}} -------------------------------------------------------------------------------- /app/components/gh-blog-url.js: -------------------------------------------------------------------------------- 1 | import Component from '@ember/component'; 2 | import classic from 'ember-classic-decorator'; 3 | import {inject as service} from '@ember/service'; 4 | import {tagName} from '@ember-decorators/component'; 5 | 6 | @classic 7 | @tagName('') 8 | export default class GhBlogUrl extends Component { 9 | @service config; 10 | } 11 | -------------------------------------------------------------------------------- /app/components/gh-browser-preview.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import {inject as service} from '@ember/service'; 3 | 4 | export default class GhBrowserPreview extends Component { 5 | @service settings; 6 | } 7 | -------------------------------------------------------------------------------- /app/components/gh-canvas-header.hbs: -------------------------------------------------------------------------------- 1 |
5 |
6 | {{yield}} 7 |
8 |
-------------------------------------------------------------------------------- /app/components/gh-cm-editor.hbs: -------------------------------------------------------------------------------- 1 | {{!-- display a standard textarea whilst waiting for CodeMirror to load/initialize --}} 2 | 7 | -------------------------------------------------------------------------------- /app/components/gh-custom-view-title.hbs: -------------------------------------------------------------------------------- 1 |

2 | {{or @title "No @title provided"}} 3 | {{#if this.customViews.activeView}} 4 | {{svg-jar "arrow-right"}} 5 | {{this.customViews.activeView.name}} 6 | {{/if}} 7 |

-------------------------------------------------------------------------------- /app/components/gh-custom-view-title.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import {inject as service} from '@ember/service'; 3 | 4 | export default class GhCustomViewTitleComponent extends Component { 5 | @service customViews; 6 | @service router; 7 | } 8 | -------------------------------------------------------------------------------- /app/components/gh-email-preview-link.hbs: -------------------------------------------------------------------------------- 1 | 2 | {{~#if (has-block)~}} 3 | {{~yield~}} 4 | {{else}} 5 | {{~or @data.subject @data.email.subject~}} 6 | {{/if}} 7 | -------------------------------------------------------------------------------- /app/components/gh-email-preview-link.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import EmailPreviewModal from './modals/email-preview'; 3 | import {action} from '@ember/object'; 4 | import {inject as service} from '@ember/service'; 5 | 6 | export default class GhEmailPreviewLink extends Component { 7 | @service modals; 8 | 9 | @action 10 | openPreview(event) { 11 | event.preventDefault(); 12 | return this.modals.open(EmailPreviewModal, this.args.data); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/components/gh-error-message.hbs: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /app/components/gh-feature-flag.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{{yield}}} 4 | -------------------------------------------------------------------------------- /app/components/gh-file-upload.hbs: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /app/components/gh-form-group.js: -------------------------------------------------------------------------------- 1 | import ValidationStatusContainer from 'ghost-admin/components/gh-validation-status-container'; 2 | import classic from 'ember-classic-decorator'; 3 | import {classNames} from '@ember-decorators/component'; 4 | 5 | @classic 6 | @classNames('form-group') 7 | export default class GhFormGroup extends ValidationStatusContainer {} 8 | -------------------------------------------------------------------------------- /app/components/gh-html-iframe.hbs: -------------------------------------------------------------------------------- 1 |
6 | 7 | 8 |
-------------------------------------------------------------------------------- /app/components/gh-infinity-loader.js: -------------------------------------------------------------------------------- 1 | import InfinityLoader from 'ember-infinity/components/infinity-loader'; 2 | import classic from 'ember-classic-decorator'; 3 | 4 | @classic 5 | export default class GhInfinityLoader extends InfinityLoader {} 6 | -------------------------------------------------------------------------------- /app/components/gh-input-with-select/suggested-option.hbs: -------------------------------------------------------------------------------- 1 | {{@option.text}}{{svg-jar "add"}} -------------------------------------------------------------------------------- /app/components/gh-link-to-custom-views-index.hbs: -------------------------------------------------------------------------------- 1 | 9 | {{yield}} 10 | -------------------------------------------------------------------------------- /app/components/gh-loading-spinner.hbs: -------------------------------------------------------------------------------- 1 | {{#if this.showSpinner}} 2 |
3 |
4 |
5 | {{/if}} -------------------------------------------------------------------------------- /app/components/gh-member-avatar.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{this.initials}} 4 |
5 | {{#if this.avatarImage}} 6 | {{or @member.name @member.email}} 7 | {{/if}} 8 |
9 | -------------------------------------------------------------------------------- /app/components/gh-member-details.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import {inject as service} from '@ember/service'; 3 | 4 | export default class extends Component { 5 | @service settings; 6 | } -------------------------------------------------------------------------------- /app/components/gh-member-single-label-input.hbs: -------------------------------------------------------------------------------- 1 | 2 | 9 | {{svg-jar "arrow-down-small"}} 10 | 11 | -------------------------------------------------------------------------------- /app/components/gh-members-filter-count.hbs: -------------------------------------------------------------------------------- 1 | {{this.memberCount}} -------------------------------------------------------------------------------- /app/components/gh-members-segment-count.hbs: -------------------------------------------------------------------------------- 1 | {{#if this.session.user.isAdmin}} 2 | 7 | {{format-number this.segmentTotal}} {{gh-pluralize this.segmentTotal "member" without-count=true}} 8 | 9 | {{/if}} -------------------------------------------------------------------------------- /app/components/gh-mobile-nav-bar.js: -------------------------------------------------------------------------------- 1 | import Component from '@ember/component'; 2 | import classic from 'ember-classic-decorator'; 3 | import {classNames, tagName} from '@ember-decorators/component'; 4 | import {inject as service} from '@ember/service'; 5 | 6 | @classic 7 | @tagName('nav') 8 | @classNames('gh-mobile-nav-bar') 9 | export default class GhMobileNavBar extends Component { 10 | @service ui; 11 | } 12 | -------------------------------------------------------------------------------- /app/components/gh-nav-menu.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/components/gh-notifications.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/components/gh-notifications.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import {inject as service} from '@ember/service'; 3 | 4 | export default class GhNotifications extends Component { 5 | @service notifications; 6 | } 7 | -------------------------------------------------------------------------------- /app/components/gh-progress-bar.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
9 |
10 |
11 | -------------------------------------------------------------------------------- /app/components/gh-psm-authors-input.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/components/gh-psm-tags-input.hbs: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /app/components/gh-psm-visibility-input.hbs: -------------------------------------------------------------------------------- 1 | 2 | 9 | {{svg-jar "arrow-down-small"}} 10 | -------------------------------------------------------------------------------- /app/components/gh-recipient-filter-count.hbs: -------------------------------------------------------------------------------- 1 | {{#if @filter}} 2 | 3 | {{else}} 4 | 0 members 5 | {{/if}} 6 | -------------------------------------------------------------------------------- /app/components/gh-scroll-trigger.hbs: -------------------------------------------------------------------------------- 1 |
2 | {{yield}} 3 |
-------------------------------------------------------------------------------- /app/components/gh-simplemde.hbs: -------------------------------------------------------------------------------- 1 | {{yield}} 2 | -------------------------------------------------------------------------------- /app/components/gh-site-iframe.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/components/gh-text-input.hbs: -------------------------------------------------------------------------------- 1 | {{yield}} -------------------------------------------------------------------------------- /app/components/gh-text-input.js: -------------------------------------------------------------------------------- 1 | import TextField from '@ember/component/text-field'; 2 | import TextInputMixin from 'ghost-admin/mixins/text-input'; 3 | import classic from 'ember-classic-decorator'; 4 | import {classNames} from '@ember-decorators/component'; 5 | 6 | @classic 7 | @classNames('gh-input') 8 | export default class GhTextInput extends TextField.extend(TextInputMixin) {} 9 | -------------------------------------------------------------------------------- /app/components/gh-theme-error-li.js: -------------------------------------------------------------------------------- 1 | import Component from '@ember/component'; 2 | import classic from 'ember-classic-decorator'; 3 | import {action} from '@ember/object'; 4 | import {tagName} from '@ember-decorators/component'; 5 | 6 | @classic 7 | @tagName('') 8 | export default class GhThemeErrorLi extends Component { 9 | error = null; 10 | showDetails = false; 11 | 12 | @action 13 | toggleDetails() { 14 | this.toggleProperty('showDetails'); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/components/gh-token-input/label-selected-item.hbs: -------------------------------------------------------------------------------- 1 | {{@option.name}} 2 | -------------------------------------------------------------------------------- /app/components/gh-token-input/label-token.hbs: -------------------------------------------------------------------------------- 1 | {{yield}} 2 | -------------------------------------------------------------------------------- /app/components/gh-token-input/label-token.js: -------------------------------------------------------------------------------- 1 | import DraggableObject from 'ember-drag-drop/components/draggable-object'; 2 | import classic from 'ember-classic-decorator'; 3 | import {attributeBindings, classNames} from '@ember-decorators/component'; 4 | 5 | @classic 6 | @attributeBindings('title') 7 | @classNames('label-token') 8 | export default class LabelToken extends DraggableObject { 9 | title = 'Label'; 10 | } 11 | -------------------------------------------------------------------------------- /app/components/gh-token-input/suggested-option.hbs: -------------------------------------------------------------------------------- 1 | {{@option.text}} 2 | -------------------------------------------------------------------------------- /app/components/gh-token-input/tag-token.hbs: -------------------------------------------------------------------------------- 1 | {{yield}} 2 | -------------------------------------------------------------------------------- /app/components/gh-url-input.hbs: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /app/components/gh-url-preview.hbs: -------------------------------------------------------------------------------- 1 | {{this.url}} 2 | -------------------------------------------------------------------------------- /app/components/gh-user-active.hbs: -------------------------------------------------------------------------------- 1 | {{yield this}} 2 | -------------------------------------------------------------------------------- /app/components/gh-user-invited.hbs: -------------------------------------------------------------------------------- 1 | {{yield this}} 2 | -------------------------------------------------------------------------------- /app/components/gh-user-list-item.js: -------------------------------------------------------------------------------- 1 | import Component from '@ember/component'; 2 | import classic from 'ember-classic-decorator'; 3 | import {tagName} from '@ember-decorators/component'; 4 | 5 | @classic 6 | @tagName('') 7 | export default class GhUserListItem extends Component {} 8 | -------------------------------------------------------------------------------- /app/components/gh-view-title.hbs: -------------------------------------------------------------------------------- 1 | 2 | {{yield}} 3 | -------------------------------------------------------------------------------- /app/components/gh-view-title.js: -------------------------------------------------------------------------------- 1 | import Component from '@ember/component'; 2 | import classic from 'ember-classic-decorator'; 3 | import {classNames, tagName} from '@ember-decorators/component'; 4 | import {inject as service} from '@ember/service'; 5 | 6 | @classic 7 | @tagName('h2') 8 | @classNames('view-title') 9 | export default class GhViewTitle extends Component { 10 | @service ui; 11 | } 12 | -------------------------------------------------------------------------------- /app/components/inputs/select.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/components/inputs/select/option.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/components/koenig-react-editor.hbs: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /app/components/liquid-container.js: -------------------------------------------------------------------------------- 1 | import LiquidContainer from 'liquid-fire/components/liquid-container'; 2 | import config from 'ghost-admin/config/environment'; 3 | 4 | export default LiquidContainer.extend({ 5 | init() { 6 | this._super(...arguments); 7 | 8 | if (config.environment === 'test') { 9 | this.growDuration = 5; 10 | } 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /app/components/member/activity-feed-empty.hbs: -------------------------------------------------------------------------------- 1 |
2 |
{{svg-jar "no-data-list"}}
3 |

Activity

4 |

5 | All events related to this member will be shown here. 6 |

7 |
-------------------------------------------------------------------------------- /app/components/members-activity/member-filter-trigger.hbs: -------------------------------------------------------------------------------- 1 |
Filter member {{svg-jar "arrow-down-small"}}
2 | -------------------------------------------------------------------------------- /app/components/modal-early-access.js: -------------------------------------------------------------------------------- 1 | import ModalComponent from 'ghost-admin/components/modal-base'; 2 | 3 | export default ModalComponent.extend({}); 4 | -------------------------------------------------------------------------------- /app/components/modal-leave-settings.js: -------------------------------------------------------------------------------- 1 | import ModalComponent from 'ghost-admin/components/modal-base'; 2 | import RSVP from 'rsvp'; 3 | 4 | export default ModalComponent.extend({ 5 | actions: { 6 | confirm() { 7 | this.confirm(); 8 | this.send('closeModal'); 9 | } 10 | }, 11 | 12 | // Allowed actions 13 | confirm: () => RSVP.resolve() 14 | }); 15 | -------------------------------------------------------------------------------- /app/components/modal-markdown-help.js: -------------------------------------------------------------------------------- 1 | import ModalComponent from 'ghost-admin/components/modal-base'; 2 | 3 | export default ModalComponent.extend({ 4 | actions: { 5 | // noop - we don't want the enter key doing anything 6 | confirm() {} 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /app/components/modal-regenerate-token.js: -------------------------------------------------------------------------------- 1 | import ModalComponent from 'ghost-admin/components/modal-base'; 2 | 3 | export default ModalComponent.extend({ 4 | actions: { 5 | confirm() { 6 | this.confirm(); 7 | this.send('closeModal'); 8 | } 9 | } 10 | }); 11 | -------------------------------------------------------------------------------- /app/components/modal-upgrade-unsuspend-user-host-limit.js: -------------------------------------------------------------------------------- 1 | import ModalComponent from 'ghost-admin/components/modal-base'; 2 | import {inject as service} from '@ember/service'; 3 | 4 | export default ModalComponent.extend({ 5 | router: service(), 6 | 7 | actions: { 8 | upgrade() { 9 | this.router.transitionTo('pro'); 10 | }, 11 | 12 | confirm() { 13 | this.send('upgrade'); 14 | } 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /app/components/modal-whats-new.js: -------------------------------------------------------------------------------- 1 | import ModalComponent from 'ghost-admin/components/modal-base'; 2 | import {inject as service} from '@ember/service'; 3 | 4 | export default ModalComponent.extend({ 5 | whatsNew: service(), 6 | 7 | confirm() {}, 8 | 9 | actions: { 10 | // noop - enter key shouldn't do anything 11 | confirm() {} 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /app/components/modals/search.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/components/modals/search.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import {action} from '@ember/object'; 3 | 4 | export default class SearchModal extends Component { 5 | @action 6 | focusFirstInput(mouseEvent) { 7 | mouseEvent.target.querySelector('input')?.focus(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /app/components/power-select-vertical-collection-options.js: -------------------------------------------------------------------------------- 1 | import OptionsComponent from 'ember-power-select/components/power-select/options'; 2 | 3 | export default class PowerSelectVerticalCollectionOptions extends OptionsComponent {} 4 | -------------------------------------------------------------------------------- /app/components/react-component.hbs: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /app/components/settings/members/archive-tier.hbs: -------------------------------------------------------------------------------- 1 | {{#if this.tier.active}} 2 | {{#unless this.tier.isNew}} 3 | 9 | {{/unless}} 10 | {{else}} 11 | 17 | {{/if}} -------------------------------------------------------------------------------- /app/controllers/billing.js: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | import classic from 'ember-classic-decorator'; 3 | import {alias} from '@ember/object/computed'; 4 | 5 | @classic 6 | export default class BillingController extends Controller { 7 | queryParams = ['action']; 8 | action = null; 9 | 10 | @alias('model') 11 | guid; 12 | } 13 | -------------------------------------------------------------------------------- /app/controllers/designsandbox.js: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | 3 | export default class DesignsandboxController extends Controller { 4 | } -------------------------------------------------------------------------------- /app/controllers/editor/edit-loading.js: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | import {inject as service} from '@ember/service'; 3 | export default class EditLoadingController extends Controller { 4 | @service ui; 5 | } 6 | -------------------------------------------------------------------------------- /app/controllers/home.js: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | import {tracked} from '@glimmer/tracking'; 3 | 4 | export default class HomeController extends Controller { 5 | queryParams = ['firstStart']; 6 | 7 | @tracked firstStart = null; 8 | } 9 | -------------------------------------------------------------------------------- /app/controllers/pages-loading.js: -------------------------------------------------------------------------------- 1 | import PostsLoadingController from './posts-loading'; 2 | import classic from 'ember-classic-decorator'; 3 | import {inject as controller} from '@ember/controller'; 4 | import {inject as service} from '@ember/service'; 5 | 6 | /* eslint-disable ghost/ember/alias-model-in-controller */ 7 | @classic 8 | export default class PagesLoadingController extends PostsLoadingController { 9 | @controller('pages') 10 | postsController; 11 | 12 | @service ui; 13 | } 14 | -------------------------------------------------------------------------------- /app/controllers/react-editor/edit-loading.js: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | import {inject as service} from '@ember/service'; 3 | export default class ReactEditLoadingController extends Controller { 4 | @service ui; 5 | } 6 | -------------------------------------------------------------------------------- /app/controllers/settings/design.js: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | 3 | export default class SettingsDesignController extends Controller { 4 | } 5 | -------------------------------------------------------------------------------- /app/controllers/settings/design/change-theme/install.js: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | import {tracked} from '@glimmer/tracking'; 3 | 4 | export default class InstallThemeController extends Controller { 5 | queryParams = ['source', 'ref']; 6 | 7 | @tracked source = ''; 8 | @tracked ref = ''; 9 | } 10 | -------------------------------------------------------------------------------- /app/controllers/settings/staff/user-loading.js: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | import {inject as service} from '@ember/service'; 3 | 4 | export default class StaffUserLoadingController extends Controller { 5 | @service session; 6 | } -------------------------------------------------------------------------------- /app/controllers/site.js: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | import classic from 'ember-classic-decorator'; 3 | import {alias} from '@ember/object/computed'; 4 | 5 | @classic 6 | export default class SiteController extends Controller { 7 | @alias('model') 8 | guid; 9 | } 10 | -------------------------------------------------------------------------------- /app/errors/email-failed-error.js: -------------------------------------------------------------------------------- 1 | export default class EmailFailedError extends Error { 2 | constructor(message) { 3 | super(message); 4 | this.name = 'EmailFailedError'; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /app/errors/member-import-error.js: -------------------------------------------------------------------------------- 1 | export default class EmailFailedError extends Error { 2 | constructor({message, context, type = 'error'}) { 3 | super(message); 4 | this.name = 'MemberImportError'; 5 | this.context = context; 6 | this.type = type; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /app/helpers/accent-color-background.js: -------------------------------------------------------------------------------- 1 | import Helper from '@ember/component/helper'; 2 | import classic from 'ember-classic-decorator'; 3 | import {htmlSafe} from '@ember/template'; 4 | import {inject as service} from '@ember/service'; 5 | 6 | @classic 7 | export default class AccentColorBackgroundHelper extends Helper { 8 | @service config; 9 | 10 | compute() { 11 | const color = this.get('config.accent_color'); 12 | return htmlSafe(`background: ${color};`); 13 | } 14 | } -------------------------------------------------------------------------------- /app/helpers/author-names.js: -------------------------------------------------------------------------------- 1 | import {helper} from '@ember/component/helper'; 2 | import {isEmpty} from '@ember/utils'; 3 | 4 | export function authorNames([authors]/*, hash*/) { 5 | if (!authors || isEmpty(authors)) { 6 | return; 7 | } 8 | 9 | return authors.mapBy('name').join(', '); 10 | } 11 | 12 | export default helper(authorNames); 13 | -------------------------------------------------------------------------------- /app/helpers/background-image-style.js: -------------------------------------------------------------------------------- 1 | import {helper} from '@ember/component/helper'; 2 | import {htmlSafe} from '@ember/template'; 3 | 4 | export function backgroundImageStyle([url]/*, hash*/) { 5 | if (url) { 6 | let safeUrl = encodeURI(decodeURI(url)); 7 | return htmlSafe(`background-image: url(${safeUrl});`); 8 | } 9 | 10 | return ''; 11 | } 12 | 13 | export default helper(backgroundImageStyle); 14 | -------------------------------------------------------------------------------- /app/helpers/capitalize-first-letter.js: -------------------------------------------------------------------------------- 1 | import {helper} from '@ember/component/helper'; 2 | 3 | export function capitalizeFirstLetter(string) { 4 | if (typeof string !== 'string' || string.length === 0) { 5 | return string; 6 | } 7 | return string.charAt(0).toUpperCase() + string.slice(1); 8 | } 9 | 10 | export default helper(([string]) => capitalizeFirstLetter(string)); 11 | -------------------------------------------------------------------------------- /app/helpers/currency-symbol.js: -------------------------------------------------------------------------------- 1 | import Helper from '@ember/component/helper'; 2 | import {getSymbol} from 'ghost-admin/utils/currency'; 3 | import {inject as service} from '@ember/service'; 4 | 5 | export default class CurrencySymbolHelper extends Helper { 6 | @service feature; 7 | 8 | compute([currency]) { 9 | if (currency) { 10 | return getSymbol(currency); 11 | } 12 | return ''; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/helpers/enable-developer-experiments.js: -------------------------------------------------------------------------------- 1 | import Helper from '@ember/component/helper'; 2 | import {inject as service} from '@ember/service'; 3 | 4 | export default class EnableDeveloperExperimentsHelper extends Helper { 5 | @service config; 6 | 7 | compute() { 8 | return this.config.get('enableDeveloperExperiments'); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/helpers/feature.js: -------------------------------------------------------------------------------- 1 | import Helper from '@ember/component/helper'; 2 | import {inject as service} from '@ember/service'; 3 | 4 | export default class EnableDeveloperExperimentsHelper extends Helper { 5 | @service feature; 6 | 7 | compute([featureFlag]) { 8 | return this.feature.get(featureFlag); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/helpers/first-name.js: -------------------------------------------------------------------------------- 1 | import {helper} from '@ember/component/helper'; 2 | 3 | export function firstName([name = '']) { 4 | return name.split(' ')[0]; 5 | } 6 | 7 | export default helper(firstName); -------------------------------------------------------------------------------- /app/helpers/format-number.js: -------------------------------------------------------------------------------- 1 | import {helper} from '@ember/component/helper'; 2 | 3 | export function formatNumber(number, options) { 4 | if (number === '' || number === null || number === undefined) { 5 | return; 6 | } 7 | 8 | return Number(number).toLocaleString(undefined, options); 9 | } 10 | 11 | export default helper(function ([number]/*, hash*/) { 12 | return formatNumber(number); 13 | }); 14 | -------------------------------------------------------------------------------- /app/helpers/full-email-address.js: -------------------------------------------------------------------------------- 1 | import Helper from '@ember/component/helper'; 2 | import {inject as service} from '@ember/service'; 3 | 4 | export default class FullEmailAddressHelper extends Helper { 5 | @service config; 6 | 7 | compute([email = '']) { 8 | if (email.indexOf('@') > -1) { 9 | return email; 10 | } 11 | 12 | return `${email}@${this.config.emailDomain}`; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/helpers/get-setting.js: -------------------------------------------------------------------------------- 1 | import Helper from '@ember/component/helper'; 2 | import {get} from '@ember/object'; 3 | import {inject as service} from '@ember/service'; 4 | 5 | export default class GetSetting extends Helper { 6 | @service settings; 7 | 8 | compute([key = '']) { 9 | return get(this.settings, key); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/helpers/hex-contrast.js: -------------------------------------------------------------------------------- 1 | import {Color, textColorForBackgroundColor} from '@tryghost/color-utils'; 2 | import {helper} from '@ember/component/helper'; 3 | 4 | export default helper(function hexContrast([hex]) { 5 | return textColorForBackgroundColor(Color(hex)).hex(); 6 | }); 7 | -------------------------------------------------------------------------------- /app/helpers/highlighted-text.js: -------------------------------------------------------------------------------- 1 | import {helper} from '@ember/component/helper'; 2 | import {htmlSafe} from '@ember/template'; 3 | 4 | export function highlightedText([text, termToHighlight]) { 5 | // replace any non-word character with an escaped character 6 | let sanitisedTerm = termToHighlight.replace(new RegExp(/\W/ig), '\\$&'); 7 | 8 | return htmlSafe(text.replace(new RegExp(sanitisedTerm, 'ig'), '$&')); 9 | } 10 | 11 | export default helper(highlightedText); 12 | -------------------------------------------------------------------------------- /app/helpers/integration-icon-style.js: -------------------------------------------------------------------------------- 1 | import {helper} from '@ember/component/helper'; 2 | import {htmlSafe} from '@ember/template'; 3 | 4 | export function integrationLogoStyle([integration]/*, hash*/) { 5 | if (integration.iconImage) { 6 | let style = `background-image:url(${integration.iconImage});background-size:36px;`; 7 | return htmlSafe(style); 8 | } 9 | } 10 | 11 | export default helper(integrationLogoStyle); 12 | -------------------------------------------------------------------------------- /app/helpers/is-moment-today.js: -------------------------------------------------------------------------------- 1 | import Helper from '@ember/component/helper'; 2 | import moment from 'moment'; 3 | import {inject as service} from '@ember/service'; 4 | 5 | export default class IsMomentToday extends Helper { 6 | @service settings; 7 | 8 | compute([date]) { 9 | const today = moment().tz(this.settings.get('timezone')); 10 | const dateMoment = moment.tz(date, this.settings.get('timezone')); 11 | 12 | return dateMoment.isSame(today, 'day'); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/helpers/moment-site-tz.js: -------------------------------------------------------------------------------- 1 | import Helper from '@ember/component/helper'; 2 | import moment from 'moment'; 3 | import {inject as service} from '@ember/service'; 4 | 5 | export default class MomentSiteTz extends Helper { 6 | @service settings; 7 | 8 | compute([date]) { 9 | return moment.tz(date, this.settings.get('timezone')); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/helpers/noop.js: -------------------------------------------------------------------------------- 1 | import {helper} from '@ember/component/helper'; 2 | 3 | export default helper(function noop() { 4 | return () => {}; 5 | }); 6 | -------------------------------------------------------------------------------- /app/helpers/post-author-names.js: -------------------------------------------------------------------------------- 1 | import {helper} from '@ember/component/helper'; 2 | 3 | export default helper(function postAuthorNames([post]/*, hash*/) { 4 | return (post?.authors || []).map(author => author.name || author.email).join(', '); 5 | }); 6 | -------------------------------------------------------------------------------- /app/helpers/query-selector.js: -------------------------------------------------------------------------------- 1 | export default function (selector) { 2 | const elem = document.querySelector(selector); 3 | 4 | if (!elem) { 5 | console.warn(`{{query-selector}} could not find an element matching "${selector}"`); //eslint-disable-line 6 | } 7 | 8 | return elem; 9 | } 10 | -------------------------------------------------------------------------------- /app/helpers/set-has.js: -------------------------------------------------------------------------------- 1 | import {helper} from '@ember/component/helper'; 2 | 3 | export default helper(function ([set, key]) { 4 | return set.has(key); 5 | }); 6 | -------------------------------------------------------------------------------- /app/helpers/site-icon-style.js: -------------------------------------------------------------------------------- 1 | import Helper from '@ember/component/helper'; 2 | import classic from 'ember-classic-decorator'; 3 | import {htmlSafe} from '@ember/template'; 4 | import {inject as service} from '@ember/service'; 5 | 6 | @classic 7 | export default class SiteIconStyleHelper extends Helper { 8 | @service config; 9 | 10 | compute() { 11 | const icon = this.get('config.icon') || 'https://static.ghost.org/v4.0.0/images/ghost-orb-2.png'; 12 | return htmlSafe(`background-image: url(${icon})`); 13 | } 14 | } -------------------------------------------------------------------------------- /app/helpers/toggle-feature.js: -------------------------------------------------------------------------------- 1 | import Helper from '@ember/component/helper'; 2 | import {inject as service} from '@ember/service'; 3 | 4 | export default class ToggleFeature extends Helper { 5 | @service feature; 6 | 7 | compute([featureFlag]) { 8 | return () => { 9 | const flag = !!this.feature.get(featureFlag); 10 | this.feature.set(featureFlag, !flag); 11 | }; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/helpers/ui-btn-span.js: -------------------------------------------------------------------------------- 1 | import {btnStyles} from './ui-btn'; 2 | import {helper} from '@ember/component/helper'; 3 | 4 | export function uiBtnSpan([style], hash) { 5 | return btnStyles(Object.assign({}, {style}, hash)).span; 6 | } 7 | 8 | export default helper(uiBtnSpan); 9 | -------------------------------------------------------------------------------- /app/initializers/upgrade-status.js: -------------------------------------------------------------------------------- 1 | export function initialize(application) { 2 | application.inject('route', 'upgradeStatus', 'service:upgrade-status'); 3 | } 4 | 5 | export default { 6 | name: 'upgrade-status', 7 | initialize 8 | }; 9 | -------------------------------------------------------------------------------- /app/mixins/dropdown-mixin.js: -------------------------------------------------------------------------------- 1 | import Evented from '@ember/object/evented'; 2 | import Mixin from '@ember/object/mixin'; 3 | 4 | /* 5 | Dropdowns and their buttons are evented and do not propagate clicks. 6 | */ 7 | export default Mixin.create(Evented, { 8 | classNameBindings: ['isOpen:open:closed'], 9 | isOpen: false, 10 | 11 | click(event) { 12 | this._super(event); 13 | 14 | return event.stopPropagation(); 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /app/models/action.js: -------------------------------------------------------------------------------- 1 | import Model, {attr} from '@ember-data/model'; 2 | 3 | export default Model.extend({ 4 | resourceId: attr('string'), 5 | resourceType: attr('string'), 6 | actorId: attr('string'), 7 | actorType: attr('string'), 8 | event: attr('string'), 9 | context: attr('json-string'), 10 | createdAtUTC: attr('moment-utc') 11 | }); 12 | -------------------------------------------------------------------------------- /app/models/api-key.js: -------------------------------------------------------------------------------- 1 | import Model, {attr, belongsTo} from '@ember-data/model'; 2 | 3 | export default Model.extend({ 4 | type: attr('string'), 5 | secret: attr('string'), 6 | lastSeenAtUTC: attr('moment-utc'), 7 | createdAtUTC: attr('moment-utc'), 8 | createdBy: attr('number'), 9 | updatedAtUTC: attr('moment-utc'), 10 | updatedBy: attr('number'), 11 | 12 | integration: belongsTo('integration') 13 | }); 14 | -------------------------------------------------------------------------------- /app/models/custom-theme-setting-list.js: -------------------------------------------------------------------------------- 1 | import Model, {hasMany} from '@ember-data/model'; 2 | 3 | export default Model.extend({ 4 | customThemeSettings: hasMany('custom-theme-setting') 5 | }); 6 | -------------------------------------------------------------------------------- /app/models/custom-theme-setting.js: -------------------------------------------------------------------------------- 1 | import Model, {attr} from '@ember-data/model'; 2 | 3 | export default Model.extend({ 4 | key: attr('string'), 5 | type: attr('string'), 6 | options: attr(), 7 | default: attr('string'), 8 | value: attr(), 9 | group: attr('string') 10 | }); 11 | -------------------------------------------------------------------------------- /app/models/member-tier.js: -------------------------------------------------------------------------------- 1 | import EmberObject from '@ember/object'; 2 | 3 | export default EmberObject.extend({ 4 | name: 'Name of the tier', 5 | slug: 'Slug for the tier' 6 | }); 7 | -------------------------------------------------------------------------------- /app/models/notification.js: -------------------------------------------------------------------------------- 1 | import Model, {attr} from '@ember-data/model'; 2 | 3 | export default Model.extend({ 4 | custom: attr('boolean'), 5 | dismissible: attr('boolean'), 6 | key: attr('string'), 7 | message: attr('string'), 8 | status: attr('string'), 9 | type: attr('string') 10 | }); 11 | -------------------------------------------------------------------------------- /app/models/page.js: -------------------------------------------------------------------------------- 1 | import PostModel from './post'; 2 | 3 | export default PostModel.extend({ 4 | displayName: 'page' 5 | }); 6 | -------------------------------------------------------------------------------- /app/models/role.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | import Model, {attr} from '@ember-data/model'; 3 | import {computed} from '@ember/object'; 4 | 5 | export default Model.extend({ 6 | name: attr('string'), 7 | description: attr('string'), 8 | createdAtUTC: attr('moment-utc'), 9 | updatedAtUTC: attr('moment-utc'), 10 | createdBy: attr('number'), 11 | updatedBy: attr('number'), 12 | 13 | lowerCaseName: computed('name', function () { 14 | return (this.name || '').toLocaleLowerCase(); 15 | }) 16 | }); 17 | -------------------------------------------------------------------------------- /app/models/snippet.js: -------------------------------------------------------------------------------- 1 | import Model, {attr} from '@ember-data/model'; 2 | import ValidationEngine from 'ghost-admin/mixins/validation-engine'; 3 | 4 | export default Model.extend(ValidationEngine, { 5 | validationType: 'snippet', 6 | 7 | name: attr('string'), 8 | mobiledoc: attr('json-string'), 9 | createdAtUTC: attr('moment-utc'), 10 | updatedAtUTC: attr('moment-utc') 11 | }); 12 | -------------------------------------------------------------------------------- /app/modifiers/autofocus.js: -------------------------------------------------------------------------------- 1 | import {modifier} from 'ember-modifier'; 2 | 3 | export default modifier(element => element.focus(), {eager: false}); 4 | -------------------------------------------------------------------------------- /app/modifiers/scroll-top.js: -------------------------------------------------------------------------------- 1 | import getScrollParent from 'ghost-admin/utils/get-scroll-parent'; 2 | import {modifier} from 'ember-modifier'; 3 | 4 | export default modifier((element) => { 5 | getScrollParent(element).scrollTop = 0; 6 | }, {eager: false}); 7 | -------------------------------------------------------------------------------- /app/routes/admin.js: -------------------------------------------------------------------------------- 1 | import AuthenticatedRoute from './authenticated'; 2 | import {inject as service} from '@ember/service'; 3 | 4 | export default class AdminRoute extends AuthenticatedRoute { 5 | @service session; 6 | 7 | beforeModel() { 8 | super.beforeModel(...arguments); 9 | 10 | if (!this.session.user.isAdmin) { 11 | return this.transitionTo('home'); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/routes/authenticated.js: -------------------------------------------------------------------------------- 1 | import Route from '@ember/routing/route'; 2 | import {inject as service} from '@ember/service'; 3 | 4 | export default class AuthenticatedRoute extends Route { 5 | @service session; 6 | 7 | beforeModel(transition) { 8 | this.session.requireAuthentication(transition, 'signin'); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/routes/editor/index.js: -------------------------------------------------------------------------------- 1 | import AuthenticatedRoute from 'ghost-admin/routes/authenticated'; 2 | 3 | export default class IndexRoute extends AuthenticatedRoute { 4 | beforeModel() { 5 | super.beforeModel(...arguments); 6 | this.replaceWith('editor.new', 'post'); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /app/routes/error404.js: -------------------------------------------------------------------------------- 1 | import Route from '@ember/routing/route'; 2 | 3 | export default class Error404Route extends Route { 4 | controllerName = 'error'; 5 | templateName = 'error'; 6 | 7 | model() { 8 | return { 9 | status: 404 10 | }; 11 | } 12 | 13 | buildRouteInfoMetadata() { 14 | return { 15 | titleToken: 'Error' 16 | }; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/routes/launch.js: -------------------------------------------------------------------------------- 1 | import AuthenticatedRoute from 'ghost-admin/routes/authenticated'; 2 | import {inject as service} from '@ember/service'; 3 | 4 | export default class LaunchRoute extends AuthenticatedRoute { 5 | @service session; 6 | 7 | beforeModel() { 8 | super.beforeModel(...arguments); 9 | if (!this.session.user.isOwnerOnly) { 10 | return this.transitionTo('home'); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/routes/member/new.js: -------------------------------------------------------------------------------- 1 | import MemberRoute from '../member'; 2 | 3 | export default class NewMemberRoute extends MemberRoute { 4 | controllerName = 'member'; 5 | templateName = 'member'; 6 | } 7 | -------------------------------------------------------------------------------- /app/routes/members-activity.js: -------------------------------------------------------------------------------- 1 | import AdminRoute from 'ghost-admin/routes/admin'; 2 | 3 | export default class MembersActivityRoute extends AdminRoute { 4 | } 5 | -------------------------------------------------------------------------------- /app/routes/members/import.js: -------------------------------------------------------------------------------- 1 | import AdminRoute from 'ghost-admin/routes/admin'; 2 | 3 | export default class MembersImportRoute extends AdminRoute {} 4 | -------------------------------------------------------------------------------- /app/routes/offer/new.js: -------------------------------------------------------------------------------- 1 | import OfferRoute from '../offer'; 2 | 3 | export default class NewOfferRoute extends OfferRoute { 4 | controllerName = 'offer'; 5 | templateName = 'offer'; 6 | } 7 | -------------------------------------------------------------------------------- /app/routes/pages.js: -------------------------------------------------------------------------------- 1 | import PostsRoute from './posts'; 2 | 3 | export default class PagesRoute extends PostsRoute { 4 | modelName = 'page'; 5 | 6 | buildRouteInfoMetadata() { 7 | return { 8 | titleToken: 'Pages' 9 | }; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/routes/react-editor/index.js: -------------------------------------------------------------------------------- 1 | import AuthenticatedRoute from 'ghost-admin/routes/authenticated'; 2 | 3 | export default class IndexRoute extends AuthenticatedRoute { 4 | beforeModel() { 5 | super.beforeModel(...arguments); 6 | this.replaceWith('react-editor.new', 'post'); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /app/routes/settings.js: -------------------------------------------------------------------------------- 1 | import AuthenticatedRoute from 'ghost-admin/routes/authenticated'; 2 | import {inject as service} from '@ember/service'; 3 | 4 | export default class SettingsRoute extends AuthenticatedRoute { 5 | @service session; 6 | 7 | beforeModel() { 8 | super.beforeModel(...arguments); 9 | 10 | const user = this.session.user; 11 | 12 | if (!user.isAdmin) { 13 | return this.transitionTo('settings.staff.user', user); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/routes/settings/design/change-theme.js: -------------------------------------------------------------------------------- 1 | import AdminRoute from 'ghost-admin/routes/admin'; 2 | import {action} from '@ember/object'; 3 | import {inject as service} from '@ember/service'; 4 | 5 | export default class ChangeThemeRoute extends AdminRoute { 6 | @service store; 7 | 8 | model() { 9 | return this.store.findAll('theme'); 10 | } 11 | 12 | @action 13 | willTransition() { 14 | this.controllerFor('settings.design.change-theme').reset(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/routes/settings/integration/webhooks/edit.js: -------------------------------------------------------------------------------- 1 | import AdminRoute from 'ghost-admin/routes/admin'; 2 | 3 | export default class EditRoute extends AdminRoute { 4 | model(params) { 5 | let integration = this.modelFor('settings.integration'); 6 | let webhook = integration.webhooks.findBy('id', params.webhook_id); 7 | return webhook; 8 | } 9 | 10 | deactivate() { 11 | super.deactivate(...arguments); 12 | this.controller.reset(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/routes/settings/integration/webhooks/new.js: -------------------------------------------------------------------------------- 1 | import AdminRoute from 'ghost-admin/routes/admin'; 2 | 3 | export default class NewRoute extends AdminRoute { 4 | model() { 5 | let integration = this.modelFor('settings.integration'); 6 | return this.store.createRecord('webhook', {integration}); 7 | } 8 | 9 | deactivate() { 10 | super.deactivate(...arguments); 11 | this.controller.webhook.rollbackAttributes(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/routes/settings/members-email.js: -------------------------------------------------------------------------------- 1 | import AdminRoute from 'ghost-admin/routes/admin'; 2 | 3 | export default class MembersEmailRoute extends AdminRoute { 4 | beforeModel() { 5 | // Moved to newsletters 6 | return this.replaceWith('settings.newsletters'); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /app/routes/settings/theme-install.js: -------------------------------------------------------------------------------- 1 | import Route from '@ember/routing/route'; 2 | 3 | export default class InstallThemeRoute extends Route { 4 | redirect(model, transition) { 5 | this.transitionTo('settings.design.change-theme.install', {queryParams: transition.to.queryParams}); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /app/routes/settings/tier/new.js: -------------------------------------------------------------------------------- 1 | import TierRoute from '../tier'; 2 | 3 | export default class NewTierRoute extends TierRoute { 4 | controllerName = 'settings.tier'; 5 | templateName = 'settings.tier'; 6 | } 7 | -------------------------------------------------------------------------------- /app/routes/setup/index.js: -------------------------------------------------------------------------------- 1 | import Route from '@ember/routing/route'; 2 | 3 | export default class IndexRoute extends Route { 4 | beforeModel() { 5 | super.beforeModel(...arguments); 6 | this.transitionTo('setup.one'); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /app/routes/signout.js: -------------------------------------------------------------------------------- 1 | import AuthenticatedRoute from 'ghost-admin/routes/authenticated'; 2 | import {inject as service} from '@ember/service'; 3 | 4 | export default class SignoutRoute extends AuthenticatedRoute { 5 | @service notifications; 6 | 7 | afterModel/*model, transition*/() { 8 | this.notifications.clearAll(); 9 | this.session.invalidate(); 10 | } 11 | 12 | buildRouteInfoMetadata() { 13 | return { 14 | titleToken: 'Sign Out' 15 | }; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/routes/site.js: -------------------------------------------------------------------------------- 1 | import AuthenticatedRoute from 'ghost-admin/routes/authenticated'; 2 | import {inject as service} from '@ember/service'; 3 | 4 | export default class SiteRoute extends AuthenticatedRoute { 5 | @service config; 6 | @service settings; 7 | @service ui; 8 | 9 | _hasLoggedIn = false; 10 | 11 | model() { 12 | return (new Date()).valueOf(); 13 | } 14 | 15 | buildRouteInfoMetadata() { 16 | return { 17 | titleToken: 'Site' 18 | }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/routes/tag/new.js: -------------------------------------------------------------------------------- 1 | import TagRoute from '../tag'; 2 | 3 | export default class NewRoute extends TagRoute { 4 | controllerName = 'tag'; 5 | templateName = 'tag'; 6 | } 7 | -------------------------------------------------------------------------------- /app/routes/whatsnew.js: -------------------------------------------------------------------------------- 1 | import AuthenticatedRoute from 'ghost-admin/routes/authenticated'; 2 | 3 | export default class WhatsnewRoute extends AuthenticatedRoute { 4 | buildRouteInfoMetadata() { 5 | return { 6 | titleToken: `What's new?` 7 | }; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /app/serializers/action.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | import ApplicationSerializer from 'ghost-admin/serializers/application'; 3 | 4 | export default class ActionSerializer extends ApplicationSerializer { 5 | attrs = { 6 | createdAtUTC: {key: 'created_at'} 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /app/serializers/api-key.js: -------------------------------------------------------------------------------- 1 | import ApplicationSerializer from 'ghost-admin/serializers/application'; 2 | 3 | export default class ApiKeySerializer extends ApplicationSerializer { 4 | attrs = { 5 | lastSeenAtUTC: {key: 'last_seen_at'}, 6 | createdAtUTC: {key: 'created_at'}, 7 | updatedAtUTC: {key: 'updated_at'} 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /app/serializers/email.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | import ApplicationSerializer from 'ghost-admin/serializers/application'; 3 | 4 | export default class EmailSerializer extends ApplicationSerializer { 5 | attrs = { 6 | createdAtUTC: {key: 'created_at'}, 7 | updatedAtUTC: {key: 'updated_at'}, 8 | submittedAtUTC: {key: 'submitted_at'} 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /app/serializers/integration.js: -------------------------------------------------------------------------------- 1 | import ApplicationSerializer from './application'; 2 | import {EmbeddedRecordsMixin} from '@ember-data/serializer/rest'; 3 | 4 | export default class IntegrationSerializer extends ApplicationSerializer.extend(EmbeddedRecordsMixin) { 5 | attrs = { 6 | apiKeys: {embedded: 'always'}, 7 | webhooks: {embedded: 'always'}, 8 | createdAtUTC: {key: 'created_at'}, 9 | updatedAtUTC: {key: 'updated_at'} 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /app/serializers/invite.js: -------------------------------------------------------------------------------- 1 | import ApplicationSerializer from 'ghost-admin/serializers/application'; 2 | 3 | export default class InviteSerializer extends ApplicationSerializer { 4 | attrs = { 5 | createdAtUTC: {key: 'created_at'}, 6 | updatedAtUTC: {key: 'updated_at'} 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /app/serializers/notification.js: -------------------------------------------------------------------------------- 1 | import ApplicationSerializer from 'ghost-admin/serializers/application'; 2 | 3 | export default class NotificationSerializer extends ApplicationSerializer { 4 | attrs = { 5 | key: {key: 'location'} 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /app/serializers/role.js: -------------------------------------------------------------------------------- 1 | import ApplicationSerializer from 'ghost-admin/serializers/application'; 2 | 3 | export default class ActionSerializer extends ApplicationSerializer { 4 | attrs = { 5 | createdAtUTC: {key: 'created_at'}, 6 | updatedAtUTC: {key: 'updated_at'} 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /app/serializers/theme.js: -------------------------------------------------------------------------------- 1 | import ApplicationSerializer from './application'; 2 | 3 | export default class Theme extends ApplicationSerializer { 4 | primaryKey = 'name'; 5 | } 6 | -------------------------------------------------------------------------------- /app/serializers/tier.js: -------------------------------------------------------------------------------- 1 | import ApplicationSerializer from './application'; 2 | 3 | export default class TierSerializer extends ApplicationSerializer { 4 | serialize() { 5 | let json = super.serialize(...arguments); 6 | 7 | if (json?.monthly_price) { 8 | json.monthly_price = Math.round(json.monthly_price); 9 | } 10 | 11 | if (json?.yearly_price) { 12 | json.yearly_price = Math.round(json.yearly_price); 13 | } 14 | 15 | return json; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/serializers/webhook.js: -------------------------------------------------------------------------------- 1 | import ApplicationSerializer from './application'; 2 | 3 | export default class ActionSerializer extends ApplicationSerializer { 4 | attrs = { 5 | lastTriggeredAtUTC: {key: 'last_triggered_at'}, 6 | createdAtUTC: {key: 'created_at'}, 7 | updatedAtUTC: {key: 'updated_at'} 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /app/services/event-bus.js: -------------------------------------------------------------------------------- 1 | import Evented from '@ember/object/evented'; 2 | import Service from '@ember/service'; 3 | import classic from 'ember-classic-decorator'; 4 | 5 | @classic 6 | export default class EventBusService extends Service.extend(Evented) { 7 | publish() { 8 | return this.trigger(...arguments); 9 | } 10 | 11 | subscribe() { 12 | return this.on(...arguments); 13 | } 14 | 15 | unsubscribe() { 16 | return this.off(...arguments); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/services/ghost-paths.js: -------------------------------------------------------------------------------- 1 | import Service from '@ember/service'; 2 | import classic from 'ember-classic-decorator'; 3 | import ghostPaths from 'ghost-admin/utils/ghost-paths'; 4 | 5 | @classic 6 | export default class GhostPathsService extends Service.extend(ghostPaths()) {} 7 | -------------------------------------------------------------------------------- /app/services/media.js: -------------------------------------------------------------------------------- 1 | import Service from '@ember/service'; 2 | import classic from 'ember-classic-decorator'; 3 | 4 | // dummy service to account for not having the ember-responsive dependency 5 | // available for ember-light-table (we don't use it so no need for the dep) 6 | // see https://github.com/offirgolan/ember-light-table/issues/576 7 | @classic 8 | export default class MediaService extends Service {} 9 | -------------------------------------------------------------------------------- /app/services/utils.js: -------------------------------------------------------------------------------- 1 | import Service from '@ember/service'; 2 | 3 | export default class UtilsService extends Service { 4 | downloadFile(url) { 5 | let iframe = document.getElementById('iframeDownload'); 6 | 7 | if (!iframe) { 8 | iframe = document.createElement('iframe'); 9 | iframe.id = 'iframeDownload'; 10 | iframe.style.display = 'none'; 11 | document.body.append(iframe); 12 | } 13 | 14 | iframe.setAttribute('src', url); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/styles/components/codemirror.css: -------------------------------------------------------------------------------- 1 | /* Exclude CodeMirror from the border-box reset to avoid scrollbar problems */ 2 | .CodeMirror, 3 | .CodeMirror * { 4 | box-sizing: initial; 5 | } 6 | 7 | /* Re-apply CodeMirror's content-box styles overridden by above reset */ 8 | .CodeMirror-scroll, 9 | .CodeMirror-sizer, 10 | .CodeMirror-gutter, 11 | .CodeMirror-gutters, 12 | .CodeMirror-linenumber { 13 | box-sizing: content-box; 14 | } 15 | 16 | .CodeMirror-linenumber { 17 | min-width: 14px; 18 | } 19 | -------------------------------------------------------------------------------- /app/styles/components/stacks.css: -------------------------------------------------------------------------------- 1 | /* Lists that are open on the sides 2 | /* ------------------------------------------------- */ 3 | .gh-stack { 4 | display: flex; 5 | flex-direction: column; 6 | } 7 | 8 | .gh-stack-item { 9 | margin-left: 0 !important; 10 | margin-right: 0 !important; 11 | padding-left: 0 !important; 12 | padding-right: 0 !important; 13 | } 14 | 15 | .gh-stack-item .gh-setting-content { 16 | margin-right: 24px; 17 | } -------------------------------------------------------------------------------- /app/styles/spirit/_box-shadow.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | BOX-SHADOW 4 | 5 | Media Query Extensions: 6 | -ns = not-small 7 | -m = medium 8 | -l = large 9 | 10 | */ 11 | 12 | .shadow-1 { box-shadow: var(--shadow-1); } 13 | .shadow-2 { box-shadow: var(--shadow-2); } 14 | .shadow-3 { box-shadow: var(--shadow-3); } -------------------------------------------------------------------------------- /app/styles/spirit/_code.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | CODE 4 | 5 | */ 6 | 7 | .pre { 8 | overflow-x: auto; 9 | overflow-y: hidden; 10 | overflow: scroll; 11 | } 12 | -------------------------------------------------------------------------------- /app/styles/spirit/_colors.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Ghost color variables 4 | Modifications use PostCSS color mod function: https://github.com/postcss/postcss-color-mod-function 5 | 6 | Note: unused, moved all colors to global.css. Long term we wanna get rid of Spirit 7 | 8 | */ 9 | -------------------------------------------------------------------------------- /app/styles/spirit/_custom-styles-dark.css: -------------------------------------------------------------------------------- 1 | .bg-grouped-table { 2 | background: #191b1f; 3 | } 4 | 5 | .highlight-whitegrey:hover { 6 | background-color: color-mod(var(--whitegrey) a(60%) s(+5%)) !important; 7 | } -------------------------------------------------------------------------------- /app/styles/spirit/_debug-children.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | DEBUG CHILDREN 4 | Docs: http://tachyons.io/docs/debug/ 5 | 6 | Just add the debug class to any element to see outlines on its 7 | children. 8 | 9 | */ 10 | 11 | .debug * { outline: 1px solid gold; } 12 | .debug-white * { outline: 1px solid white; } 13 | .debug-black * { outline: 1px solid black; } 14 | 15 | -------------------------------------------------------------------------------- /app/styles/spirit/_images.css: -------------------------------------------------------------------------------- 1 | /* Responsive images! */ 2 | 3 | img { max-width: 100%; } 4 | -------------------------------------------------------------------------------- /app/styles/spirit/_links.css: -------------------------------------------------------------------------------- 1 | .link { 2 | text-decoration: none; 3 | transition: color .15s ease-in; 4 | } 5 | 6 | .link:link, 7 | .link:visited { 8 | transition: color .15s ease-in; 9 | } 10 | .link:hover { 11 | transition: color .15s ease-in; 12 | } 13 | .link:active { 14 | transition: color .15s ease-in; 15 | } 16 | .link:focus { 17 | transition: color .15s ease-in; 18 | outline: 1px dotted currentColor; 19 | } 20 | -------------------------------------------------------------------------------- /app/styles/spirit/_lists.css: -------------------------------------------------------------------------------- 1 | .list { list-style-type: none; } 2 | -------------------------------------------------------------------------------- /app/styles/spirit/_module-template.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MODULE NAME 4 | 5 | Use this scaffolding to create or extend your own modules with tachyons 6 | style architecture. 7 | 8 | */ 9 | 10 | 11 | @media (--breakpoint-not-small) { 12 | 13 | } 14 | 15 | @media (--breakpoint-medium) { 16 | 17 | } 18 | 19 | @media (--breakpoint-large) { 20 | 21 | } 22 | 23 | -------------------------------------------------------------------------------- /app/styles/spirit/_opacity.css: -------------------------------------------------------------------------------- 1 | .o-100 { opacity: 1; } 2 | .o-90 { opacity: .9; } 3 | .o-80 { opacity: .8; } 4 | .o-70 { opacity: .7; } 5 | .o-60 { opacity: .6; } 6 | .o-50 { opacity: .5; } 7 | .o-40 { opacity: .4; } 8 | .o-30 { opacity: .3; } 9 | .o-20 { opacity: .2; } 10 | .o-10 { opacity: .1; } 11 | .o-05 { opacity: .05; } 12 | .o-025 { opacity: .025; } 13 | .o-0 { opacity: 0; } 14 | -------------------------------------------------------------------------------- /app/styles/spirit/_tables.css: -------------------------------------------------------------------------------- 1 | .collapse { 2 | border-collapse: collapse; 3 | border-spacing: 0; 4 | } 5 | 6 | .striped:nth-child(odd) { 7 | border-bottom: 1px solid var(--whitegrey); 8 | } 9 | 10 | .striped:nth-child(even) { 11 | background-color: var(--whitegrey-l2); 12 | border-bottom: 1px solid var(--whitegrey); 13 | } 14 | 15 | th, td { 16 | vertical-align: top; 17 | } 18 | -------------------------------------------------------------------------------- /app/styles/spirit/spirit-dark.css: -------------------------------------------------------------------------------- 1 | @import "./spirit.css"; 2 | 3 | /* Import dark theme overrides */ 4 | @import "./_colors-dark.css"; 5 | @import "./_custom-styles-dark.css"; 6 | -------------------------------------------------------------------------------- /app/templates/editor/edit-loading.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
6 | -------------------------------------------------------------------------------- /app/templates/members/import.hbs: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /app/templates/react-editor/edit-loading.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
6 | -------------------------------------------------------------------------------- /app/templates/settings/integration/webhooks/edit.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/templates/settings/integration/webhooks/new.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/templates/settings/labs-loading.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 | Settings 5 | {{svg-jar "arrow-right"}} 6 | Labs 7 |

8 |
9 | 10 |
11 | 12 |
13 |
14 | -------------------------------------------------------------------------------- /app/templates/site.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/templates/tags-loading.hbs: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Tags

4 |
5 | New tag 6 |
7 |
8 | 9 |
10 | 11 |
12 |
13 | -------------------------------------------------------------------------------- /app/transforms/json-string.js: -------------------------------------------------------------------------------- 1 | import Transform from '@ember-data/serializer/transform'; 2 | 3 | export default class JsonString extends Transform { 4 | deserialize(serialized) { 5 | let _serialized = serialized === '' ? null : serialized; 6 | return JSON.parse(_serialized); 7 | } 8 | 9 | serialize(deserialized) { 10 | return deserialized ? JSON.stringify(deserialized) : null; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /app/transforms/moment-date.js: -------------------------------------------------------------------------------- 1 | import Transform from '@ember-data/serializer/transform'; 2 | import moment from 'moment'; 3 | 4 | export default class MomentDate extends Transform { 5 | deserialize(serialized) { 6 | if (serialized) { 7 | return moment(serialized); 8 | } 9 | return serialized; 10 | } 11 | 12 | serialize(deserialized) { 13 | if (deserialized) { 14 | return moment(deserialized).toDate(); 15 | } 16 | return deserialized; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/transforms/raw.js: -------------------------------------------------------------------------------- 1 | import Transform from '@ember-data/serializer/transform'; 2 | 3 | export default class Raw extends Transform { 4 | deserialize(serialized) { 5 | return serialized; 6 | } 7 | 8 | serialize(deserialized) { 9 | return deserialized; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/utils/copy-text-to-clipboard.js: -------------------------------------------------------------------------------- 1 | export default function copyTextToClipboard(text) { 2 | navigator.clipboard.writeText(text); 3 | } 4 | -------------------------------------------------------------------------------- /app/utils/ctrl-or-cmd.js: -------------------------------------------------------------------------------- 1 | export default navigator.userAgent.indexOf('Mac') !== -1 ? 'command' : 'ctrl'; 2 | -------------------------------------------------------------------------------- /app/utils/flatten-grouped-options.js: -------------------------------------------------------------------------------- 1 | export default function flattenGroupedOptions(options) { 2 | const flatOptions = []; 3 | 4 | function getOptions(option) { 5 | if (option.options) { 6 | return option.options.forEach(getOptions); 7 | } 8 | 9 | flatOptions.push(option); 10 | } 11 | 12 | options.forEach(getOptions); 13 | 14 | return flatOptions; 15 | } 16 | -------------------------------------------------------------------------------- /app/utils/get-scroll-parent.js: -------------------------------------------------------------------------------- 1 | export default function getScrollParent(node) { 2 | const isElement = node instanceof HTMLElement; 3 | const overflowY = isElement && window.getComputedStyle(node).overflowY; 4 | const isScrollable = overflowY !== 'visible' && overflowY !== 'hidden'; 5 | 6 | if (!node) { 7 | return null; 8 | } else if (isScrollable && node.scrollHeight >= node.clientHeight) { 9 | return node; 10 | } 11 | 12 | return getScrollParent(node.parentNode) || document.body; 13 | } 14 | -------------------------------------------------------------------------------- /app/utils/isNumber.js: -------------------------------------------------------------------------------- 1 | // isNumber function from lodash 2 | 3 | const {toString} = Object.prototype; 4 | 5 | export default function (value) { 6 | return typeof value === 'number' 7 | || value 8 | && typeof value === 'object' 9 | && toString.call(value) === '[object Number]' 10 | || false; 11 | } 12 | -------------------------------------------------------------------------------- /app/utils/slug-url.js: -------------------------------------------------------------------------------- 1 | import {isBlank} from '@ember/utils'; 2 | 3 | export default function SlugUrl(url, query) { 4 | if (query && !isBlank(query.slug)) { 5 | url += `slug/${query.slug}/`; 6 | delete query.slug; 7 | } 8 | 9 | return url; 10 | } 11 | -------------------------------------------------------------------------------- /app/utils/window-proxy.js: -------------------------------------------------------------------------------- 1 | export default { 2 | changeLocation(url) { 3 | window.location = url; 4 | }, 5 | 6 | replaceLocation(url) { 7 | window.location.replace(url); 8 | }, 9 | 10 | replaceState(params, title, url) { 11 | window.history.replaceState(params, title, url); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /app/validators/custom-view.js: -------------------------------------------------------------------------------- 1 | import BaseValidator from './base'; 2 | import {isBlank} from '@ember/utils'; 3 | 4 | export default BaseValidator.create({ 5 | properties: ['name'], 6 | 7 | name(model) { 8 | if (isBlank(model.name)) { 9 | model.errors.add('name', 'Please enter a name'); 10 | model.hasValidated.pushObject('name'); 11 | this.invalidate(); 12 | } 13 | } 14 | }); 15 | -------------------------------------------------------------------------------- /app/validators/label.js: -------------------------------------------------------------------------------- 1 | import BaseValidator from './base'; 2 | import {isBlank} from '@ember/utils'; 3 | 4 | export default BaseValidator.create({ 5 | properties: ['name'], 6 | 7 | name(model) { 8 | if (isBlank(model.name)) { 9 | model.errors.add('name', 'Please enter a name'); 10 | model.hasValidated.pushObject('name'); 11 | this.invalidate(); 12 | } 13 | } 14 | }); -------------------------------------------------------------------------------- /app/validators/signup.js: -------------------------------------------------------------------------------- 1 | import NewUserValidator from 'ghost-admin/validators/new-user'; 2 | 3 | export default NewUserValidator.create(); 4 | -------------------------------------------------------------------------------- /config/coverage.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | module.exports = { 3 | parallel: true, 4 | reporters: ['cobertura'] 5 | }; 6 | -------------------------------------------------------------------------------- /config/optional-features.json: -------------------------------------------------------------------------------- 1 | { 2 | "application-template-wrapper": false, 3 | "jquery-integration": true, 4 | "template-only-glimmer-components": true 5 | } 6 | -------------------------------------------------------------------------------- /config/targets.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | const browsers = [ 4 | 'last 2 Chrome versions', 5 | 'last 2 Firefox versions', 6 | 'last 3 Safari versions', 7 | 'last 2 Edge versions' 8 | ]; 9 | 10 | module.exports = { 11 | browsers 12 | }; 13 | -------------------------------------------------------------------------------- /lib/asset-delivery/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asset-delivery", 3 | "keywords": [ 4 | "ember-addon" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /lib/koenig-editor/addon/components/kg-action-bar.hbs: -------------------------------------------------------------------------------- 1 |
    2 | {{yield}} 3 |
-------------------------------------------------------------------------------- /lib/koenig-editor/addon/components/koenig-alt-input.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/koenig-editor/addon/components/koenig-caption-input.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/koenig-editor/addon/components/koenig-card-embed/nft.js: -------------------------------------------------------------------------------- 1 | import Component from '@ember/component'; 2 | import classic from 'ember-classic-decorator'; 3 | 4 | @classic 5 | export default class Nft extends Component { 6 | payload = null; 7 | } 8 | -------------------------------------------------------------------------------- /lib/koenig-editor/addon/components/koenig-card-hr.hbs: -------------------------------------------------------------------------------- 1 | 11 |
12 |
-------------------------------------------------------------------------------- /lib/koenig-editor/addon/components/koenig-card-hr.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | 3 | export default class KoenigCardHrComponent extends Component { 4 | constructor() { 5 | super(...arguments); 6 | this.args.registerComponent(this); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /lib/koenig-editor/addon/components/koenig-card-image/selector-tenor/gif.hbs: -------------------------------------------------------------------------------- 1 | 2 |
3 | {{@gif.description}} 4 |
5 |
-------------------------------------------------------------------------------- /lib/koenig-editor/addon/components/koenig-card-paywall.hbs: -------------------------------------------------------------------------------- 1 | 11 |
Free public preview / Only visible to members
12 |
-------------------------------------------------------------------------------- /lib/koenig-editor/addon/components/koenig-card-paywall.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | 3 | export default class KoenigCardPaywallComponent extends Component { 4 | constructor() { 5 | super(...arguments); 6 | this.args.registerComponent(this); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /lib/koenig-editor/addon/components/koenig-media-selector.hbs: -------------------------------------------------------------------------------- 1 |
2 | {{yield (hash 3 | insertCard=this.insertCard 4 | close=this.args.close 5 | )}} 6 |
-------------------------------------------------------------------------------- /lib/koenig-editor/addon/components/koenig-menu-content.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import {action} from '@ember/object'; 3 | 4 | export default class KoenigMenuContentComponent extends Component { 5 | @action 6 | scrollIntoView(element, [doScroll]) { 7 | if (doScroll) { 8 | element.scrollIntoView({ 9 | behavior: 'smooth', 10 | block: 'nearest' 11 | }); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/koenig-editor/addon/components/koenig-settings-panel.hbs: -------------------------------------------------------------------------------- 1 |
2 | {{yield}} 3 |
-------------------------------------------------------------------------------- /lib/koenig-editor/addon/helpers/clean-basic-html.js: -------------------------------------------------------------------------------- 1 | import cleanBasicHtml from '@tryghost/kg-clean-basic-html'; 2 | import {helper} from '@ember/component/helper'; 3 | import {isArray} from '@ember/array'; 4 | 5 | export function cleanBasicHtmlHelper(html = '') { 6 | if (isArray(html)) { 7 | html = html[0] || ''; 8 | } 9 | 10 | return cleanBasicHtml(html); 11 | } 12 | 13 | export default helper(cleanBasicHtmlHelper); 14 | -------------------------------------------------------------------------------- /lib/koenig-editor/addon/options/basic-html-parser-plugins.js: -------------------------------------------------------------------------------- 1 | export function removeBR(node, builder, {addMarkerable, nodeFinished}) { 2 | if (node.nodeType !== 1 || node.tagName !== 'BR') { 3 | return; 4 | } 5 | 6 | addMarkerable(builder.createMarker(' ')); 7 | nodeFinished(); 8 | } 9 | 10 | export default [ 11 | removeBR 12 | ]; 13 | -------------------------------------------------------------------------------- /lib/koenig-editor/addon/utils/extract-audio-metadata.js: -------------------------------------------------------------------------------- 1 | export default function extractAudioMetadata(file) { 2 | return new Promise((resolve) => { 3 | let audio = new Audio(); 4 | let duration; 5 | const mimeType = file.type; 6 | audio.onloadedmetadata = function () { 7 | duration = audio.duration; 8 | resolve({ 9 | duration, 10 | mimeType 11 | }); 12 | }; 13 | audio.src = URL.createObjectURL(file); 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /lib/koenig-editor/addon/utils/markup-utils.js: -------------------------------------------------------------------------------- 1 | export function getLinkMarkupFromRange(range) { 2 | let {headMarker, tailMarker} = range; 3 | if (headMarker && (headMarker === tailMarker || headMarker.next === tailMarker)) { 4 | return tailMarker.markups.findBy('tagName', 'a'); 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /lib/koenig-editor/addon/utils/prettify-file-name.js: -------------------------------------------------------------------------------- 1 | export default function prettifyFileName(filename) { 2 | if (!filename || typeof filename !== 'string') { 3 | return ''; 4 | } 5 | let updatedName = filename.split('.').slice(0, -1).join('.').replace(/[-_]/g,' ').replace(/[^\w\s]+/g,'').replace(/\s\s+/g, ' '); 6 | return updatedName.charAt(0).toUpperCase() + updatedName.slice(1); 7 | } 8 | -------------------------------------------------------------------------------- /lib/koenig-editor/addon/utils/snippet-icon.js: -------------------------------------------------------------------------------- 1 | export default function snippetIcon(snippet) { 2 | let {mobiledoc} = snippet; 3 | 4 | if (mobiledoc.cards.length === 0) { 5 | return 'koenig/kg-card-type-snippet-text'; 6 | } 7 | 8 | let hasRichText = mobiledoc.sections.some((section) => { 9 | return section[0] !== 10; 10 | }); 11 | 12 | if (hasRichText) { 13 | return 'koenig/kg-card-type-snippet-combination'; 14 | } 15 | 16 | return 'koenig/kg-card-type-snippet-block'; 17 | } 18 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/kg-action-bar.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/kg-action-bar'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-alt-input.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-alt-input'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-basic-html-input.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-basic-html-input'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-basic-html-textarea.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-basic-html-textarea'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-caption-input.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-caption-input'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-card-audio.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-card-audio'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-card-before-after.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-card-before-after'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-card-bookmark.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-card-bookmark'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-card-button.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-card-button'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-card-callout.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-card-callout'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-card-code.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-card-code'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-card-email-cta.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-card-email-cta'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-card-email.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-card-email'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-card-embed.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-card-embed'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-card-embed/nft.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-card-embed/nft'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-card-file.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-card-file'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-card-gallery.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-card-gallery'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-card-header.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-card-header'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-card-hr.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-card-hr'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-card-html.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-card-html'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-card-image.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-card-image'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-card-image/selector-tenor.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-card-image/selector-tenor'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-card-image/selector-tenor/gif.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-card-image/selector-tenor/gif'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-card-markdown.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-card-markdown'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-card-paywall.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-card-paywall'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-card-product.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-card-product'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-card-toggle.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-card-toggle'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-card-video.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-card-video'; -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-card.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-card'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-editor.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-editor'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-link-input.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-link-input'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-link-toolbar.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-link-toolbar'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-media-selector.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-media-selector'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-menu-content.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-menu-content'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-plus-menu.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-plus-menu'; -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-settings-panel.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-settings-panel'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-slash-menu.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-slash-menu'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-snippet-input.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-snippet-input'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-text-replacement-html-input.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-text-replacement-html-input'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/components/koenig-toolbar.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/components/koenig-toolbar'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/helpers/card-is-available.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/helpers/card-is-available'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/helpers/clean-basic-html.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/helpers/clean-basic-html'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/helpers/kg-style.js: -------------------------------------------------------------------------------- 1 | export {default, kgStyle} from 'koenig-editor/helpers/kg-style'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/helpers/sanitize-html.js: -------------------------------------------------------------------------------- 1 | export {default, sanitizeHtml} from 'koenig-editor/helpers/sanitize-html'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/services/koenig-drag-drop-handler.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/services/koenig-drag-drop-handler'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/app/services/koenig-ui.js: -------------------------------------------------------------------------------- 1 | export {default} from 'koenig-editor/services/koenig-ui'; 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/docs/specs/popup-toolbar.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/lib/koenig-editor/docs/specs/popup-toolbar.md -------------------------------------------------------------------------------- /lib/koenig-editor/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 'use strict'; 3 | 4 | module.exports = { 5 | name: 'koenig-editor', 6 | 7 | isDevelopingAddon() { 8 | return true; 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /lib/koenig-editor/public/icons/koenig/card-indicator-email.svg: -------------------------------------------------------------------------------- 1 | Stroke 1 -------------------------------------------------------------------------------- /lib/koenig-editor/public/icons/koenig/card-indicator-html.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /lib/koenig-editor/public/icons/koenig/card-indicator-markdown.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /lib/koenig-editor/public/icons/koenig/code-block.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /lib/koenig-editor/public/icons/koenig/kg-add.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lib/koenig-editor/public/icons/koenig/kg-bold.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/public/icons/koenig/kg-card-type-divider.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /lib/koenig-editor/public/icons/koenig/kg-card-type-other.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /lib/koenig-editor/public/icons/koenig/kg-card-type-snippet-block.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /lib/koenig-editor/public/icons/koenig/kg-card-type-toggle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/koenig-editor/public/icons/koenig/kg-card-type-unsplash.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/public/icons/koenig/kg-cta-border.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lib/koenig-editor/public/icons/koenig/kg-header-full-center.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/koenig-editor/public/icons/koenig/kg-header-wide-center.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/koenig-editor/public/icons/koenig/kg-header-wide-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/koenig-editor/public/icons/koenig/kg-heading-1.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/public/icons/koenig/kg-heading-2.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/public/icons/koenig/kg-img-regular.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/public/icons/koenig/kg-img-wide.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/public/icons/koenig/kg-italic.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lib/koenig-editor/public/icons/koenig/kg-star.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/koenig-editor/public/icons/koenig/kg-thin-delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | kg-thin-delete 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /lib/koenig-editor/public/icons/koenig/kg-toggle-card-open-arrow.svg: -------------------------------------------------------------------------------- 1 | arrow-down-1 2 | -------------------------------------------------------------------------------- /mirage/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | module.exports = { 3 | rules: { 4 | 'brace-style': 'off' 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /mirage/config/api-keys.js: -------------------------------------------------------------------------------- 1 | import {paginatedResponse} from '../utils'; 2 | 3 | export default function mockApiKeys(server) { 4 | server.get('/api-keys/', paginatedResponse('api-keys')); 5 | server.post('/api-keys/'); 6 | server.put('/api-keys/:id/'); 7 | server.del('/api-keys/:id/'); 8 | } 9 | -------------------------------------------------------------------------------- /mirage/config/config.js: -------------------------------------------------------------------------------- 1 | import {isEmpty} from '@ember/utils'; 2 | 3 | export default function mockConfig(server) { 4 | server.get('/config/', function ({db}) { 5 | if (isEmpty(db.configs)) { 6 | server.loadFixtures('configs'); 7 | } 8 | 9 | return { 10 | config: db.configs.find(1) 11 | }; 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /mirage/config/custom-theme-settings.js: -------------------------------------------------------------------------------- 1 | export default function mockCustomThemeSettings(server) { 2 | server.get('/custom_theme_settings'); 3 | } 4 | -------------------------------------------------------------------------------- /mirage/config/emails.js: -------------------------------------------------------------------------------- 1 | export default function mockEmails(/* server */) { 2 | // emails are currently only returned as an embedded record in post models 3 | } 4 | -------------------------------------------------------------------------------- /mirage/config/roles.js: -------------------------------------------------------------------------------- 1 | export default function mockRoles(server) { 2 | server.get('/roles/', function ({roles}, {queryParams}) { 3 | if (queryParams.permissions === 'assign') { 4 | return roles.find([1, 2, 3, 5]); 5 | } 6 | 7 | return roles.all(); 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /mirage/config/site.js: -------------------------------------------------------------------------------- 1 | import {isEmpty} from '@ember/utils'; 2 | 3 | export default function mockSite(server) { 4 | server.get('/site/', function ({db}) { 5 | if (isEmpty(db.sites)) { 6 | server.loadFixtures('sites'); 7 | } 8 | 9 | return { 10 | site: db.sites.find(1) 11 | }; 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /mirage/config/snippets.js: -------------------------------------------------------------------------------- 1 | export default function mockSnippets(server) { 2 | server.get('/snippets/'); 3 | server.post('/snippets/'); 4 | server.put('/snippets/:id/'); 5 | server.del('/snippets/:id/'); 6 | } 7 | -------------------------------------------------------------------------------- /mirage/factories/invite.js: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | import {Factory} from 'miragejs'; 3 | 4 | export default Factory.extend({ 5 | token(i) { return `${i}-token`; }, 6 | email(i) { return `invited-user-${i}@example.com`; }, 7 | expires() { return moment.utc().add(1, 'day').valueOf(); }, 8 | createdAt() { return moment.utc().format(); }, 9 | createdBy() { return 1; }, 10 | updatedAt() { return moment.utc().format(); }, 11 | updatedBy() { return 1; }, 12 | status() { return 'sent'; } 13 | }); 14 | -------------------------------------------------------------------------------- /mirage/factories/label.js: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | import {Factory} from 'miragejs'; 3 | 4 | export default Factory.extend({ 5 | createdAt() { return moment().toISOString(); }, 6 | createdBy: 1, 7 | name(i) { return `Label ${i}`; }, 8 | slug(i) { return `label-${i}`; }, 9 | updatedAt() { return moment().toISOString(); }, 10 | updatedBy: 1, 11 | count() { 12 | // this gets updated automatically by the label serializer 13 | return {members: 0}; 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /mirage/factories/notification.js: -------------------------------------------------------------------------------- 1 | import {Factory} from 'miragejs'; 2 | 3 | export default Factory.extend({ 4 | dismissible: true, 5 | message: 'This is an alert', 6 | status: 'alert', 7 | type: 'error' 8 | }); 9 | -------------------------------------------------------------------------------- /mirage/factories/role.js: -------------------------------------------------------------------------------- 1 | import {Factory} from 'miragejs'; 2 | 3 | export default Factory.extend({ 4 | createdAt: '2013-11-25T14:48:11.000Z', 5 | createdBy: 1, 6 | description(i) { return `Role ${i}`; }, 7 | name: '', 8 | updatedAt: '2013-11-25T14:48:11.000Z', 9 | updatedBy: 1 10 | }); 11 | -------------------------------------------------------------------------------- /mirage/factories/subscription.js: -------------------------------------------------------------------------------- 1 | import {Factory} from 'miragejs'; 2 | 3 | export default Factory.extend({ 4 | }); 5 | -------------------------------------------------------------------------------- /mirage/factories/tier.js: -------------------------------------------------------------------------------- 1 | import {Factory} from 'miragejs'; 2 | 3 | export default Factory.extend({ 4 | name(i) { return `Tier ${i}`; }, 5 | description(i) { return `Description for tier ${i}`; }, 6 | active: true, 7 | slug(i) { return `tier-${i}`;}, 8 | type: 'paid', 9 | visibility: 'none', 10 | currency: 'usd', 11 | monthly_price: 500, 12 | yearly_price: 5000 13 | }); 14 | -------------------------------------------------------------------------------- /mirage/fixtures/configs.js: -------------------------------------------------------------------------------- 1 | export default [{ 2 | clientExtensions: {}, 3 | database: 'mysql', 4 | enableDeveloperExperiments: false, 5 | environment: 'development', 6 | labs: {}, 7 | mail: 'SMTP', 8 | version: '2.15.0', 9 | useGravatar: 'true' 10 | }]; 11 | -------------------------------------------------------------------------------- /mirage/fixtures/newsletters.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | export default [ 3 | { 4 | id: 1, 5 | name: 'Default newsletter', 6 | slug: 'default-newsletter', 7 | status: 'active', 8 | subscribeOnSignup: true 9 | } 10 | ]; 11 | -------------------------------------------------------------------------------- /mirage/fixtures/sites.js: -------------------------------------------------------------------------------- 1 | export default [{ 2 | title: 'Test Blog', 3 | url: `${window.location.origin}/` 4 | }]; 5 | -------------------------------------------------------------------------------- /mirage/fixtures/themes.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | name: 'casper', 4 | package: { 5 | name: 'casper', 6 | version: '1.0' 7 | }, 8 | active: true 9 | }, 10 | { 11 | name: 'foo', 12 | package: { 13 | name: 'Foo', 14 | version: '0.1' 15 | } 16 | }, 17 | { 18 | name: 'bar' 19 | } 20 | ]; 21 | -------------------------------------------------------------------------------- /mirage/models/api-key.js: -------------------------------------------------------------------------------- 1 | import {Model, belongsTo} from 'miragejs'; 2 | 3 | export default Model.extend({ 4 | integration: belongsTo() 5 | }); 6 | -------------------------------------------------------------------------------- /mirage/models/config.js: -------------------------------------------------------------------------------- 1 | import {Model} from 'miragejs'; 2 | 3 | export default Model.extend({ 4 | }); 5 | -------------------------------------------------------------------------------- /mirage/models/custom-theme-setting.js: -------------------------------------------------------------------------------- 1 | import {Model} from 'miragejs'; 2 | 3 | export default Model.extend({}); 4 | -------------------------------------------------------------------------------- /mirage/models/email.js: -------------------------------------------------------------------------------- 1 | import {Model, belongsTo} from 'miragejs'; 2 | 3 | export default Model.extend({ 4 | post: belongsTo() 5 | }); 6 | -------------------------------------------------------------------------------- /mirage/models/integration.js: -------------------------------------------------------------------------------- 1 | import {Model, hasMany} from 'miragejs'; 2 | 3 | export default Model.extend({ 4 | apiKeys: hasMany(), 5 | webhooks: hasMany() 6 | }); 7 | -------------------------------------------------------------------------------- /mirage/models/invite.js: -------------------------------------------------------------------------------- 1 | import {Model, belongsTo} from 'miragejs'; 2 | 3 | export default Model.extend({ 4 | role: belongsTo() 5 | }); 6 | -------------------------------------------------------------------------------- /mirage/models/label.js: -------------------------------------------------------------------------------- 1 | import {Model, hasMany} from 'miragejs'; 2 | 3 | export default Model.extend({ 4 | members: hasMany() 5 | }); 6 | -------------------------------------------------------------------------------- /mirage/models/member-activity-event.js: -------------------------------------------------------------------------------- 1 | import {Model, belongsTo} from 'miragejs'; 2 | 3 | export default Model.extend({ 4 | email: belongsTo(), 5 | member: belongsTo() 6 | }); 7 | -------------------------------------------------------------------------------- /mirage/models/member.js: -------------------------------------------------------------------------------- 1 | import {Model, hasMany} from 'miragejs'; 2 | 3 | export default Model.extend({ 4 | labels: hasMany(), 5 | tiers: hasMany(), 6 | newsletters: hasMany(), 7 | subscriptions: hasMany() 8 | }); 9 | -------------------------------------------------------------------------------- /mirage/models/newsletter.js: -------------------------------------------------------------------------------- 1 | import {Model, hasMany} from 'miragejs'; 2 | 3 | export default Model.extend({ 4 | members: hasMany() 5 | }); 6 | -------------------------------------------------------------------------------- /mirage/models/notification.js: -------------------------------------------------------------------------------- 1 | import {Model} from 'miragejs'; 2 | 3 | export default Model.extend({ 4 | }); 5 | -------------------------------------------------------------------------------- /mirage/models/page.js: -------------------------------------------------------------------------------- 1 | import PostModel from './post'; 2 | 3 | export default PostModel.extend({ 4 | 5 | }); 6 | -------------------------------------------------------------------------------- /mirage/models/post.js: -------------------------------------------------------------------------------- 1 | import {Model, belongsTo, hasMany} from 'miragejs'; 2 | 3 | export default Model.extend({ 4 | tags: hasMany(), 5 | authors: hasMany('user'), 6 | email: belongsTo(), 7 | newsletter: belongsTo() 8 | }); 9 | -------------------------------------------------------------------------------- /mirage/models/role.js: -------------------------------------------------------------------------------- 1 | import {Model} from 'miragejs'; 2 | 3 | export default Model.extend({ 4 | }); 5 | -------------------------------------------------------------------------------- /mirage/models/site.js: -------------------------------------------------------------------------------- 1 | import {Model} from 'miragejs'; 2 | 3 | export default Model.extend({ 4 | }); 5 | -------------------------------------------------------------------------------- /mirage/models/snippet.js: -------------------------------------------------------------------------------- 1 | import {Model} from 'miragejs'; 2 | 3 | export default Model.extend({}); 4 | -------------------------------------------------------------------------------- /mirage/models/subscriber.js: -------------------------------------------------------------------------------- 1 | import {Model} from 'miragejs'; 2 | 3 | export default Model.extend({ 4 | }); 5 | -------------------------------------------------------------------------------- /mirage/models/subscription.js: -------------------------------------------------------------------------------- 1 | import {Model, belongsTo} from 'miragejs'; 2 | 3 | export default Model.extend({ 4 | member: belongsTo(), 5 | tier: belongsTo() 6 | }); 7 | -------------------------------------------------------------------------------- /mirage/models/tag.js: -------------------------------------------------------------------------------- 1 | import {Model, hasMany} from 'miragejs'; 2 | 3 | export default Model.extend({ 4 | posts: hasMany() 5 | }); 6 | -------------------------------------------------------------------------------- /mirage/models/theme.js: -------------------------------------------------------------------------------- 1 | import {Model} from 'miragejs'; 2 | 3 | export default Model.extend({ 4 | }); 5 | -------------------------------------------------------------------------------- /mirage/models/tier.js: -------------------------------------------------------------------------------- 1 | import {Model, hasMany} from 'miragejs'; 2 | 3 | export default Model.extend({ 4 | members: hasMany() 5 | }); 6 | -------------------------------------------------------------------------------- /mirage/models/user.js: -------------------------------------------------------------------------------- 1 | import {Model, hasMany} from 'miragejs'; 2 | 3 | export default Model.extend({ 4 | // used by the serializer to determine whether 5 | // or not to include the post count 6 | postCount: false, 7 | 8 | roles: hasMany(), 9 | posts: hasMany() 10 | }); 11 | -------------------------------------------------------------------------------- /mirage/models/webhook.js: -------------------------------------------------------------------------------- 1 | import {Model, belongsTo} from 'miragejs'; 2 | 3 | export default Model.extend({ 4 | integration: belongsTo() 5 | }); 6 | -------------------------------------------------------------------------------- /mirage/serializers/integration.js: -------------------------------------------------------------------------------- 1 | import BaseSerializer from './application'; 2 | import {camelize} from '@ember/string'; 3 | 4 | export default BaseSerializer.extend({ 5 | embed: true, 6 | 7 | include(request) { 8 | if (!request.queryParams.include) { 9 | return; 10 | } 11 | return request.queryParams.include.split(',').map(camelize); 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /mirage/serializers/page.js: -------------------------------------------------------------------------------- 1 | import PostSerializer from './post'; 2 | 3 | export default PostSerializer.extend({}); 4 | -------------------------------------------------------------------------------- /mirage/serializers/post.js: -------------------------------------------------------------------------------- 1 | import BaseSerializer from './application'; 2 | 3 | export default BaseSerializer.extend({ 4 | embed: true, 5 | 6 | include(/*request*/) { 7 | let includes = []; 8 | 9 | includes.push('tags'); 10 | includes.push('authors'); 11 | 12 | return includes; 13 | } 14 | }); 15 | -------------------------------------------------------------------------------- /public/assets/fonts/Inter.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/fonts/Inter.ttf -------------------------------------------------------------------------------- /public/assets/icons/add-stroke.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/add.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/icons/align-center.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/icons/align-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/icons/arrow-down-stroke.svg: -------------------------------------------------------------------------------- 1 | arrow-down-stroke -------------------------------------------------------------------------------- /public/assets/icons/arrow-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/icons/arrow-left-stroke.svg: -------------------------------------------------------------------------------- 1 | arrow-left-stroke -------------------------------------------------------------------------------- /public/assets/icons/arrow-left-tail.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/icons/arrow-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/icons/arrow-right-stroke.svg: -------------------------------------------------------------------------------- 1 | arrow-right-stroke -------------------------------------------------------------------------------- /public/assets/icons/arrow-right-tail.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/icons/arrow-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/icons/arrow-up-stroke.svg: -------------------------------------------------------------------------------- 1 | arrow-up-stroke -------------------------------------------------------------------------------- /public/assets/icons/arrow2-down.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/bookmark-article.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/brackets.svg: -------------------------------------------------------------------------------- 1 | angle-brackets -------------------------------------------------------------------------------- /public/assets/icons/calendar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/icons/check-2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/check-circle-stroke.svg: -------------------------------------------------------------------------------- 1 | check-circle -------------------------------------------------------------------------------- /public/assets/icons/check.svg: -------------------------------------------------------------------------------- 1 | check-1 -------------------------------------------------------------------------------- /public/assets/icons/circle-ellipsis.svg: -------------------------------------------------------------------------------- 1 | navigation-menu-horizontal -------------------------------------------------------------------------------- /public/assets/icons/clock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/close-stroke.svg: -------------------------------------------------------------------------------- 1 | close -------------------------------------------------------------------------------- /public/assets/icons/close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/compass-2.svg: -------------------------------------------------------------------------------- 1 | compass-direction -------------------------------------------------------------------------------- /public/assets/icons/cross-circle.svg: -------------------------------------------------------------------------------- 1 | delete-1 -------------------------------------------------------------------------------- /public/assets/icons/dividers.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/icons/dotdotdot.svg: -------------------------------------------------------------------------------- 1 | navigation-menu-horizontal -------------------------------------------------------------------------------- /public/assets/icons/download.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/icons/email-at.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/email-body.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/email-footer.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/email-header.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/email-member.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/email-name.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/email-stroke.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/assets/icons/email-unread.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/icons/external.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/icons/facebook.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /public/assets/icons/feature-image.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/assets/icons/file-upload.svg: -------------------------------------------------------------------------------- 1 | download-circle -------------------------------------------------------------------------------- /public/assets/icons/filter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/assets/icons/firstpromoter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/icons/firstpromoter.png -------------------------------------------------------------------------------- /public/assets/icons/get-started.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/icons/ghost-logo-orb.svg: -------------------------------------------------------------------------------- 1 | 2 | Ghost Logo 3 | 10 | -------------------------------------------------------------------------------- /public/assets/icons/ghost-orb.svg: -------------------------------------------------------------------------------- 1 | 2 | Ghost Logo 3 | 10 | -------------------------------------------------------------------------------- /public/assets/icons/google-search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /public/assets/icons/grab.svg: -------------------------------------------------------------------------------- 1 | 2 | grab 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/assets/icons/graph-line.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/heart.svg: -------------------------------------------------------------------------------- 1 | heart -------------------------------------------------------------------------------- /public/assets/icons/house.svg: -------------------------------------------------------------------------------- 1 | house -------------------------------------------------------------------------------- /public/assets/icons/info.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/list-bullet.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/list-number.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/member-add.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /public/assets/icons/members.svg: -------------------------------------------------------------------------------- 1 | multiple-neutral-2 -------------------------------------------------------------------------------- /public/assets/icons/mobile-phone.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/icons/moon.svg: -------------------------------------------------------------------------------- 1 | night-moon-new-1 -------------------------------------------------------------------------------- /public/assets/icons/mute.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /public/assets/icons/navigation.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/no-data-line-chart.svg: -------------------------------------------------------------------------------- 1 | analytics-board-graph-line -------------------------------------------------------------------------------- /public/assets/icons/page.svg: -------------------------------------------------------------------------------- 1 | book-book-pages -------------------------------------------------------------------------------- /public/assets/icons/pause.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/icons/pencil-circle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/percentage.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/pin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/icons/play.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /public/assets/icons/plus.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/assets/icons/portal-icon-1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/portal-icon-2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/post.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /public/assets/icons/posts.svg: -------------------------------------------------------------------------------- 1 | pencil-write-2 -------------------------------------------------------------------------------- /public/assets/icons/reload.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/retry.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /public/assets/icons/send-email.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/sidemenu-open.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/icons/sidemenu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/icons/staff.svg: -------------------------------------------------------------------------------- 1 | single-neutral-actions-edit-1 -------------------------------------------------------------------------------- /public/assets/icons/star-filled.svg: -------------------------------------------------------------------------------- 1 | rating-star -------------------------------------------------------------------------------- /public/assets/icons/sync.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /public/assets/icons/tag.svg: -------------------------------------------------------------------------------- 1 | tags -------------------------------------------------------------------------------- /public/assets/icons/text.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/unmute.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /public/assets/icons/unsplash-heart.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/assets/icons/unsplash.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/assets/icons/unsubscribed.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/icons/upload.svg: -------------------------------------------------------------------------------- 1 | upload-bottom -------------------------------------------------------------------------------- /public/assets/icons/v-ellipsis.svg: -------------------------------------------------------------------------------- 1 | vertical-ellipsis -------------------------------------------------------------------------------- /public/assets/icons/view-site.svg: -------------------------------------------------------------------------------- 1 | layout-1 -------------------------------------------------------------------------------- /public/assets/icons/warning-stroke.svg: -------------------------------------------------------------------------------- 1 | warning -------------------------------------------------------------------------------- /public/assets/img/404-ghost.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/404-ghost.png -------------------------------------------------------------------------------- /public/assets/img/404-ghost@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/404-ghost@2x.png -------------------------------------------------------------------------------- /public/assets/img/abstract-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/abstract-2.jpg -------------------------------------------------------------------------------- /public/assets/img/abstract.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/abstract.jpg -------------------------------------------------------------------------------- /public/assets/img/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/apple-touch-icon.png -------------------------------------------------------------------------------- /public/assets/img/buffer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/buffer.png -------------------------------------------------------------------------------- /public/assets/img/community-background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/community-background.jpg -------------------------------------------------------------------------------- /public/assets/img/community.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/community.jpg -------------------------------------------------------------------------------- /public/assets/img/dashboard-feature-image.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/dashboard-feature-image.jpeg -------------------------------------------------------------------------------- /public/assets/img/dashboard/bp1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/dashboard/bp1.jpg -------------------------------------------------------------------------------- /public/assets/img/dashboard/bp2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/dashboard/bp2.jpg -------------------------------------------------------------------------------- /public/assets/img/dashboard/join-community.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/dashboard/join-community.jpg -------------------------------------------------------------------------------- /public/assets/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/favicon.ico -------------------------------------------------------------------------------- /public/assets/img/firstpromoter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/firstpromoter.png -------------------------------------------------------------------------------- /public/assets/img/footer-marketplace-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/footer-marketplace-bg.png -------------------------------------------------------------------------------- /public/assets/img/get-started.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/get-started.jpg -------------------------------------------------------------------------------- /public/assets/img/google-analytics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/google-analytics.png -------------------------------------------------------------------------------- /public/assets/img/install-welcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/install-welcome.png -------------------------------------------------------------------------------- /public/assets/img/invite-placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/invite-placeholder.png -------------------------------------------------------------------------------- /public/assets/img/large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/large.png -------------------------------------------------------------------------------- /public/assets/img/launch-wizard-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/launch-wizard-bg.png -------------------------------------------------------------------------------- /public/assets/img/loadingcat.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/loadingcat.gif -------------------------------------------------------------------------------- /public/assets/img/logos/ghost-logo-black-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/logos/ghost-logo-black-1.png -------------------------------------------------------------------------------- /public/assets/img/logos/orb-black-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/logos/orb-black-1.png -------------------------------------------------------------------------------- /public/assets/img/logos/orb-black-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/logos/orb-black-2.png -------------------------------------------------------------------------------- /public/assets/img/logos/orb-black-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/logos/orb-black-3.png -------------------------------------------------------------------------------- /public/assets/img/logos/orb-black-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/logos/orb-black-4.png -------------------------------------------------------------------------------- /public/assets/img/logos/orb-black-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/logos/orb-black-5.png -------------------------------------------------------------------------------- /public/assets/img/marketing/members-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/marketing/members-1.jpg -------------------------------------------------------------------------------- /public/assets/img/marketing/members-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/marketing/members-2.jpg -------------------------------------------------------------------------------- /public/assets/img/marketing/offers-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/marketing/offers-1.jpg -------------------------------------------------------------------------------- /public/assets/img/marketing/offers-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/marketing/offers-2.jpg -------------------------------------------------------------------------------- /public/assets/img/marketing/offers-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/marketing/offers-3.jpg -------------------------------------------------------------------------------- /public/assets/img/medium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/medium.png -------------------------------------------------------------------------------- /public/assets/img/more.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/more.png -------------------------------------------------------------------------------- /public/assets/img/newsletter-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/newsletter-1.jpg -------------------------------------------------------------------------------- /public/assets/img/newsletter-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/newsletter-2.jpg -------------------------------------------------------------------------------- /public/assets/img/orb-squircle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/orb-squircle.png -------------------------------------------------------------------------------- /public/assets/img/patreon.svg: -------------------------------------------------------------------------------- 1 | Patreon logo -------------------------------------------------------------------------------- /public/assets/img/plausible.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/plausible.png -------------------------------------------------------------------------------- /public/assets/img/resource-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/resource-1.jpg -------------------------------------------------------------------------------- /public/assets/img/slackicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/slackicon.png -------------------------------------------------------------------------------- /public/assets/img/small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/small.png -------------------------------------------------------------------------------- /public/assets/img/themes/Alto-cut.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/themes/Alto-cut.jpg -------------------------------------------------------------------------------- /public/assets/img/themes/Alto.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/themes/Alto.jpg -------------------------------------------------------------------------------- /public/assets/img/themes/Alto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/themes/Alto.png -------------------------------------------------------------------------------- /public/assets/img/themes/Bulletin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/themes/Bulletin.png -------------------------------------------------------------------------------- /public/assets/img/themes/Casper.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/themes/Casper.jpg -------------------------------------------------------------------------------- /public/assets/img/themes/Dawn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/themes/Dawn.png -------------------------------------------------------------------------------- /public/assets/img/themes/Digest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/themes/Digest.png -------------------------------------------------------------------------------- /public/assets/img/themes/Dope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/themes/Dope.png -------------------------------------------------------------------------------- /public/assets/img/themes/Ease-cut.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/themes/Ease-cut.jpg -------------------------------------------------------------------------------- /public/assets/img/themes/Ease.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/themes/Ease.jpg -------------------------------------------------------------------------------- /public/assets/img/themes/Ease.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/themes/Ease.png -------------------------------------------------------------------------------- /public/assets/img/themes/Edge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/themes/Edge.png -------------------------------------------------------------------------------- /public/assets/img/themes/Edition-cut.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/themes/Edition-cut.jpg -------------------------------------------------------------------------------- /public/assets/img/themes/Edition.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/themes/Edition.jpg -------------------------------------------------------------------------------- /public/assets/img/themes/Edition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/themes/Edition.png -------------------------------------------------------------------------------- /public/assets/img/themes/Headline.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/themes/Headline.jpg -------------------------------------------------------------------------------- /public/assets/img/themes/Journal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/themes/Journal.png -------------------------------------------------------------------------------- /public/assets/img/themes/London-cut.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/themes/London-cut.jpg -------------------------------------------------------------------------------- /public/assets/img/themes/London.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/themes/London.jpg -------------------------------------------------------------------------------- /public/assets/img/themes/Ruby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/themes/Ruby.png -------------------------------------------------------------------------------- /public/assets/img/themes/Wave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/themes/Wave.png -------------------------------------------------------------------------------- /public/assets/img/touch-icon-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/touch-icon-ipad.png -------------------------------------------------------------------------------- /public/assets/img/touch-icon-iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/touch-icon-iphone.png -------------------------------------------------------------------------------- /public/assets/img/ulysses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/ulysses.png -------------------------------------------------------------------------------- /public/assets/img/unsplash-404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/unsplash-404.png -------------------------------------------------------------------------------- /public/assets/img/user-cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/user-cover.png -------------------------------------------------------------------------------- /public/assets/img/user-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/user-image.png -------------------------------------------------------------------------------- /public/assets/img/users.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/users.png -------------------------------------------------------------------------------- /public/assets/img/zero-bounce.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/public/assets/img/zero-bounce.png -------------------------------------------------------------------------------- /tests/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | module.exports = { 3 | env: { 4 | embertest: true, 5 | mocha: true 6 | }, 7 | rules: { 8 | 'ghost/ember/no-invalid-debug-function-arguments': 'off' 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /tests/helpers/login-as-role.js: -------------------------------------------------------------------------------- 1 | import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support'; 2 | 3 | export default async function loginRole(roleName, server) { 4 | const role = server.create('role', {name: roleName}); 5 | const user = server.create('user', {roles: [role], slug: 'test-user'}); 6 | await invalidateSession(); 7 | await authenticateSession(); 8 | 9 | return user; 10 | } 11 | -------------------------------------------------------------------------------- /tests/helpers/newsletters.js: -------------------------------------------------------------------------------- 1 | export function enableNewsletters(server, enabled = true) { 2 | server.db.settings.find({key: 'editor_default_email_recipients'}) 3 | ? server.db.settings.update({key: 'editor_default_email_recipients'}, {value: (enabled ? 'visibility' : 'disabled')}) 4 | : server.create('setting', {key: 'editor_default_email_recipients', value: (enabled ? 'visibility' : 'disabled'), group: 'editor'}); 5 | } 6 | 7 | export function disableNewsletters(server) { 8 | enableNewsletters(server, false); 9 | } 10 | -------------------------------------------------------------------------------- /tests/helpers/visit.js: -------------------------------------------------------------------------------- 1 | // TODO: remove once bug is fixed in Ember 2 | // see https://github.com/emberjs/ember-test-helpers/issues/332 3 | 4 | import {visit as _visit, settled} from '@ember/test-helpers'; 5 | 6 | export async function visit(url) { 7 | try { 8 | await _visit(url); 9 | } catch (e) { 10 | if (e.message !== 'TransitionAborted') { 11 | throw e; 12 | } 13 | } 14 | 15 | await settled(); 16 | } 17 | -------------------------------------------------------------------------------- /tests/unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Admin/5c568871669ed0bf71d52a6e8abf4a5f9fe0b7fd/tests/unit/.gitkeep -------------------------------------------------------------------------------- /tests/unit/helpers/highlighted-text-test.js: -------------------------------------------------------------------------------- 1 | import { 2 | describe, 3 | it 4 | } from 'mocha'; 5 | import {expect} from 'chai'; 6 | import { 7 | highlightedText 8 | } from 'ghost-admin/helpers/highlighted-text'; 9 | 10 | describe('Unit: Helper: highlighted-text', function () { 11 | it('works', function () { 12 | let result = highlightedText(['Test', 'e']); 13 | expect(result).to.be.an('object'); 14 | expect(result.string).to.equal('Test'); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /tests/unit/models/setting-test.js: -------------------------------------------------------------------------------- 1 | import {describe, it} from 'mocha'; 2 | import {expect} from 'chai'; 3 | import {setupTest} from 'ember-mocha'; 4 | 5 | describe('Unit: Model: setting', function () { 6 | setupTest(); 7 | 8 | it('has a validation type of "setting"', function () { 9 | let model = this.owner.lookup('service:store').createRecord('setting'); 10 | 11 | expect(model.get('validationType')).to.equal('setting'); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/unit/models/tag-test.js: -------------------------------------------------------------------------------- 1 | import {describe, it} from 'mocha'; 2 | import {expect} from 'chai'; 3 | import {setupTest} from 'ember-mocha'; 4 | 5 | describe('Unit: Model: tag', function () { 6 | setupTest(); 7 | 8 | it('has a validation type of "tag"', function () { 9 | let model = this.owner.lookup('service:store').createRecord('tag'); 10 | 11 | expect(model.get('validationType')).to.equal('tag'); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/unit/routes/explore-test.js: -------------------------------------------------------------------------------- 1 | import {describe, it} from 'mocha'; 2 | import {expect} from 'chai'; 3 | import {setupTest} from 'ember-mocha'; 4 | 5 | describe('Unit | Route | explore', function () { 6 | setupTest(); 7 | 8 | it('exists', function () { 9 | let route = this.owner.lookup('route:explore'); 10 | expect(route).to.be.ok; 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /tests/unit/services/limit-test.js: -------------------------------------------------------------------------------- 1 | import {describe, it} from 'mocha'; 2 | import {expect} from 'chai'; 3 | import {setupTest} from 'ember-mocha'; 4 | 5 | describe('Unit | Service | limit', function () { 6 | setupTest(); 7 | 8 | let limitService; 9 | 10 | beforeEach(function () { 11 | limitService = this.owner.lookup('service:limit'); 12 | }); 13 | 14 | it('exists', function () { 15 | expect(limitService).to.be.ok; 16 | }); 17 | }); 18 | --------------------------------------------------------------------------------