Ghost/ghost/admin/app/templates/settings/labs.hbs

326 lines
16 KiB
Handlebars
Raw Normal View History

<section class="gh-canvas">
<GhCanvasHeader class="gh-canvas-header">
<div class="flex flex-column">
<div class="gh-canvas-breadcrumb">
<LinkTo @route="settings">
Settings
</LinkTo>
{{svg-jar "arrow-right-small"}} Labs
</div>
<h2 class="gh-canvas-title" data-test-screen-title>
Labs
</h2>
</div>
</GhCanvasHeader>
2014-12-14 20:56:04 +03:00
<section class="view-container settings-debug">
<p class="gh-box gh-box-tip">{{svg-jar "idea"}}This is a testing ground for new or experimental features. They may change, break or inexplicably disappear at any time.</p>
2017-02-17 17:10:02 +03:00
2021-02-10 15:00:07 +03:00
<div class="gh-main-section">
<h4 class="gh-main-section-header small bn">Migration options</h4>
<div class="gh-expandable {{if this.importErrors "overflow-hidden"}}">
2021-02-10 15:00:07 +03:00
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Import content</h4>
2022-11-15 16:26:19 +03:00
<p class="gh-expandable-description">Import posts from a JSON or zip file</p>
2021-02-10 15:00:07 +03:00
</div>
<LinkTo @route="settings.labs.import" class="gh-btn" data-test-link="import-content">
2022-11-15 16:26:19 +03:00
<span>Open Importer</span>
</LinkTo>
</div>
</div>
{{#if this.feature.migrateApp}}
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Migrate content</h4>
<p class="gh-expandable-description">Import your content from Substack</p>
</div>
<LinkTo @route="migrate" class="gh-btn" data-test-link="migrate">
<span>Open</span>
</LinkTo>
</div>
</div>
{{/if}}
2021-02-10 15:00:07 +03:00
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Export your content</h4>
<p class="gh-expandable-description">Download all of your posts and settings in a single, glorious JSON file</p>
</div>
<button type="button" class="gh-btn" {{action "downloadFile" "db"}}><span>Export</span></button>
</div>
</div>
2021-02-10 15:00:07 +03:00
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Delete all content</h4>
<p class="gh-expandable-description">Permanently delete all posts and tags from the database, a hard reset</p>
</div>
<button type="button" class="gh-btn gh-btn-red" {{on "click" this.confirmDeleteAll}} data-test-button="delete-all"><span>Delete</span></button>
2021-02-10 15:00:07 +03:00
</div>
</div>
2017-02-17 17:10:02 +03:00
</div>
</div>
2021-09-07 14:36:17 +03:00
<div class="gh-main-section">
<h4 class="gh-main-section-header small bn">Beta features</h4>
2021-09-07 14:36:17 +03:00
<div class="gh-expandable">
2021-02-10 15:00:07 +03:00
<div class="gh-expandable-block">
<GhUploader
@extensions={{this.redirectsFileExtensions}}
@uploadUrl="/redirects/upload/"
2021-02-10 15:00:07 +03:00
@paramName="redirects"
@onUploadSuccess={{perform this.redirectUploadResult true}}
@onUploadFailure={{perform this.redirectUploadResult false}}
as |uploader|
>
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Redirects</h4>
<p class="gh-expandable-description">Configure redirects for old or moved content, more info in <a href="https://ghost.org/tutorials/implementing-redirects/" target="_blank" rel="noopener noreferrer">the docs</a></p>
2021-02-10 15:00:07 +03:00
</div>
<div class="gh-setting-action flex flex-column items-end">
{{#if uploader.isUploading}}
{{uploader.progressBar}}
{{else}}
<button
type="button"
class="gh-btn gh-btn-icon {{if this.redirectSuccess "gh-btn-green"}} {{if this.redirectFailure "gh-btn-red"}}"
onclick={{action "triggerFileDialog"}}
data-test-button="upload-redirects"
>
<span>
{{#if this.redirectSuccess}}
{{svg-jar "check-circle"}} Uploaded
{{else if this.redirectFailure}}
{{svg-jar "retry"}} Upload Failed
{{else}}
Upload redirects YAML/JSON
2021-02-10 15:00:07 +03:00
{{/if}}
</span>
</button>
<div><a href="#" {{action "downloadFile" "redirects/download"}} data-test-link="download-redirects">Download current redirects</a></div>
2021-02-10 15:00:07 +03:00
{{/if}}
{{#each uploader.errors as |error|}}
<div class="gh-setting-error" data-test-error="redirects">{{or error.context error.message}}</div>
{{/each}}
2021-02-10 15:00:07 +03:00
<div style="display:none">
<GhFileInput @multiple={{false}} @action={{uploader.setFiles}} @accept={{this.redirectsFileMimeTypes}} data-test-file-input="redirects" />
2021-02-10 15:00:07 +03:00
</div>
</div>
</div>
2021-02-10 15:00:07 +03:00
</GhUploader>
</div>
2017-02-17 17:10:02 +03:00
2021-02-10 15:00:07 +03:00
<div class="gh-expandable-block">
<GhUploader
@extensions={{this.yamlExtension}}
@uploadUrl="/settings/routes/yaml/"
@paramName="routes"
@onUploadSuccess={{perform this.routesUploadResult true}}
@onUploadFailure={{perform this.routesUploadResult false}}
as |uploader|
>
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Routes</h4>
<p class="gh-expandable-description">Configure dynamic routing by modifying the routes.yaml file</p>
</div>
<div class="gh-setting-action flex flex-column items-end">
{{#if uploader.isUploading}}
{{uploader.progressBar}}
{{else}}
<button
type="button"
class="gh-btn gh-btn-icon {{if this.routesSuccess "gh-btn-green"}} {{if this.routesFailure "gh-btn-red"}}"
onclick={{action "triggerFileDialog"}}
data-test-button="upload-routes"
>
<span>
{{#if this.routesSuccess}}
{{svg-jar "check-circle"}} Uploaded
{{else if this.routesFailure}}
{{svg-jar "retry"}} Upload Failed
{{else}}
Upload routes YAML
{{/if}}
</span>
</button>
<div><a href="#" {{action "downloadFile" "settings/routes/yaml"}} data-test-link="download-routes">Download current routes.yaml</a></div>
{{/if}}
{{#each uploader.errors as |error|}}
<div class="gh-setting-error" data-test-error="routes">{{or error.context error.message}}</div>
{{/each}}
<div style="display:none">
<GhFileInput @multiple={{false}} @action={{uploader.setFiles}} @accept={{this.yamlAccept}} data-test-file-input="routes" />
</div>
</div>
</div>
2021-02-10 15:00:07 +03:00
</GhUploader>
</div>
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Enable Portal translations (beta)</h4>
<p class="gh-expandable-description">
Enable support for translations in the members signup/login system
</p>
</div>
<div class="for-switch">
<GhFeatureFlag @flag="i18n" />
</div>
</div>
</div>
</div>
</div>
{{#if (enable-developer-experiments)}}
<div class="gh-main-section">
<h4 class="gh-main-section-header small bn">Alpha Features</h4>
<div class="gh-expandable">
2021-09-22 14:20:15 +03:00
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">URL cache</h4>
<p class="gh-expandable-description">
Enable URL Caching
</p>
</div>
<div class="for-switch">
<GhFeatureFlag @flag="urlCache" />
</div>
</div>
</div>
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Lexical editor</h4>
<p class="gh-expandable-description">
Makes lexical editor the default when creating new posts/pages.
</p>
</div>
<div class="for-switch">
<GhFeatureFlag @flag="lexicalEditor" />
</div>
</div>
</div>
Initial setup for Lexical multiplayer websockets service (#16611) no issue Rough prototype only, current limitations: - **No persistence**. Docs are in-memory only, YJS state will be lost on server restart although it could be re-populated by clients if they reconnect without closing their local doc (needs testing/investigation) - **No tie-in with saved lexical state**. Lexical state is updated in the post model via normal API requests from Admin which can mean the multiplayer doc and the saved lexical state become out of sync but there's no detection/indication of that state at present. Will also trigger the "someone else is editing" errors because multiplayer doesn't yet override the default post update collision detection - **New posts don't start in multiplayer**. New posts don't have an ID and so can't have a respective YJS doc, after initial save we don't transition to multiplayer because the React component in Ember doesn't re-render on prop changes yet - **No tests**. Experimental code just to get something working and help answer questions for what's next Changes: - added `lexicalMultiplayer` labs flag - updated `<KoenigLexicalEditor>` to pass through the required `<KoenigComposer>` props for multiplayer when enabled - added `lexical-multiplayer` service - `init()` called during boot, used to set up the `enable()` and `disable()` methods so the flag can be toggled without restarts - when enabled it adds `upgrade` request handling to the base Ghost server - returns 404 if the URL doesn't match `/ghost/api/admin/posts/multiplayer/*` - returns 401 if a valid session cookie is not present - if everything is good, hands off to code in `y-websocket.js` that handles YJS doc creation, awareness, keepalive, etc - uses doc names in the format `${post.id}/${docId}` where `docId` is `main` for the primary document and a GUID for any sub-documents like captions and nested editors in cards - updated `SettingsBREADService` to check if the `labs` setting is changed, and enables/disables the `lexical-multiplayer` service as needed so the websockets server can be started and shutdown when toggling without requiring a restart
2023-04-12 22:24:02 +03:00
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Lexical multiplayer</h4>
<p class="gh-expandable-description">
Enables multiplayer editing in the lexical editor.
</p>
</div>
<div class="for-switch">
<GhFeatureFlag @flag="lexicalMultiplayer" />
</div>
</div>
</div>
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Webmentions</h4>
<p class="gh-expandable-description">
Allows viewing received mentions on the dashboard.
</p>
</div>
<div class="for-switch">
<GhFeatureFlag @flag="webmentions" />
</div>
</div>
</div>
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Show email errors</h4>
<p class="gh-expandable-description">
This makes email errors visible in the UI.
</p>
</div>
<div class="for-switch">
<GhFeatureFlag @flag="emailErrors" />
</div>
</div>
</div>
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Websockets</h4>
<p class="gh-expandable-description">
Test out Websockets functionality at <code>/ghost/#/websockets</code>.
</p>
</div>
<div class="for-switch">
<GhFeatureFlag @flag="websockets" />
</div>
</div>
</div>
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Stripe Automatic Tax</h4>
<p class="gh-expandable-description">
Use Stripe Automatic Tax at Stripe Checkout. Needs to be enabled in Stripe
</p>
</div>
<div class="for-switch">
<GhFeatureFlag @flag="stripeAutomaticTax" />
</div>
</div>
</div>
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Making it rain</h4>
<p class="gh-expandable-description">
Building momentum in the eyes of our publishers.
</p>
</div>
<div class="for-switch">
<GhFeatureFlag @flag="makingItRain" />
</div>
</div>
</div>
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Migrate content</h4>
<p class="gh-expandable-description">
Import content from other platforms to Ghost
</p>
</div>
<div class="for-switch">
<GhFeatureFlag @flag="migrateApp" />
</div>
</div>
</div>
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Post history</h4>
<p class="gh-expandable-description">
Enables post history revision within the editor
</p>
</div>
<div class="for-switch">
<GhFeatureFlag @flag="postHistory" />
</div>
</div>
</div>
</div>
</div>
{{/if}}
</section>
</section>