mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-24 23:48:13 -05:00
✨ Added UI for setting tag metadata (#1632)
no-issue * Updated tag model with new metadata fields * Updated Tag setting UI to handle metadata Co-authored-by: Peter Zimon <zimo@ghost.org>
This commit is contained in:
parent
2bf69f94e7
commit
ffdde799b8
6 changed files with 471 additions and 79 deletions
|
@ -1,21 +1,42 @@
|
|||
<h4 class="midlightgrey f-small fw5 ttu">Basic settings</h4>
|
||||
<div class="pa5 pt4 br4 shadow-1 bg-grouped-table mt2 flex flex-column flex-row-ns items-start justify-between gh-tag-basic-settings-form">
|
||||
<div class="order-1 flex flex-column items-start mr5 w-100 w-50-m w-two-thirds-l">
|
||||
<GhFormGroup @errors={{this.tag.errors}} @hasValidated={{this.tag.hasValidated}} @property="name">
|
||||
<label for="tag-name">Name</label>
|
||||
<GhTextInput
|
||||
@id="tag-name"
|
||||
@name="name"
|
||||
@value={{this.scratchTag.name}}
|
||||
@tabindex="1"
|
||||
@focus-out={{action "setProperty" "name" this.scratchTag.name}}
|
||||
/>
|
||||
<p class="description">
|
||||
Start with # to create internal tags
|
||||
<a href="https://ghost.org/docs/concepts/tags/#internal-tag" target="_blank" rel="noreferrer">Learn more</a>
|
||||
</p>
|
||||
<GhErrorMessage @errors={{this.tag.errors}} @property="name" />
|
||||
</GhFormGroup>
|
||||
<div class="gh-tag-settings-multiprop">
|
||||
<GhFormGroup @errors={{this.tag.errors}} @hasValidated={{this.tag.hasValidated}} @property="name" class="mr2 flex-auto">
|
||||
<label for="tag-name">Name</label>
|
||||
<GhTextInput
|
||||
@id="tag-name"
|
||||
@name="name"
|
||||
@value={{this.scratchTag.name}}
|
||||
@tabindex="1"
|
||||
@focus-out={{action "setProperty" "name" this.scratchTag.name}}
|
||||
/>
|
||||
<span class="error">
|
||||
<GhErrorMessage @errors={{this.tag.errors}} @property="name" />
|
||||
<GhErrorMessage @errors={{this.tag.errors}} @property="accentColor" data-test-error="accentColor" />
|
||||
</span>
|
||||
<p class="description">
|
||||
Start with # to create internal tags
|
||||
<a href="https://ghost.org/docs/concepts/tags/#internal-tag" target="_blank" rel="noreferrer">Learn more</a>
|
||||
</p>
|
||||
</GhFormGroup>
|
||||
|
||||
<GhFormGroup @errors={{this.tag.errors}} @hasValidated={{this.tag.hasValidated}} @property="accentColor" class="gh-tag-settings-colorcontainer">
|
||||
<label for="accent-color">Color</label>
|
||||
<div class="input-color">
|
||||
<GhTextInput
|
||||
@name="accent-color"
|
||||
@placeholder="abcdef"
|
||||
@autocorrect="off"
|
||||
@maxlength="6"
|
||||
@focus-out={{action "validateAccentColor"}}
|
||||
@value={{accentColor}}
|
||||
data-test-input="accentColor"
|
||||
/>
|
||||
<div class="color-box" style={{this.accentColorBackgroundStyle}}></div>
|
||||
</div>
|
||||
</GhFormGroup>
|
||||
</div>
|
||||
|
||||
<GhFormGroup @errors={{this.tag.errors}} @hasValidated={{this.tag.hasValidated}} @property="slug">
|
||||
<label for="tag-slug">Slug</label>
|
||||
|
@ -45,58 +66,280 @@
|
|||
</GhFormGroup>
|
||||
</div>
|
||||
<div class="order-0 mb6 mb0-ns order-2-ns w-100 w-50-m w-third-l">
|
||||
<label for="tag-image">Tag image</label>
|
||||
<GhImageUploaderWithPreview
|
||||
@image={{this.tag.featureImage}}
|
||||
@text="Upload tag image"
|
||||
@class="gh-tag-image-uploader"
|
||||
@allowUnsplash={{true}}
|
||||
@update={{action "setCoverImage"}}
|
||||
@remove={{action "clearCoverImage"}}
|
||||
/>
|
||||
<GhFormGroup @errors={{this.tag.errors}} @hasValidated={{this.tag.hasValidated}} @property="featureImage">
|
||||
<label for="tag-image">Tag image</label>
|
||||
<GhImageUploaderWithPreview
|
||||
@image={{this.tag.featureImage}}
|
||||
@text="Upload tag image"
|
||||
@class="gh-tag-image-uploader"
|
||||
@allowUnsplash={{true}}
|
||||
@update={{action "setCoverImage"}}
|
||||
@remove={{action "clearCoverImage"}}
|
||||
/>
|
||||
</GhFormGroup>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 class="midlightgrey f-small fw5 ttu mt15">Meta data</h4>
|
||||
<div class="pa5 pt4 br4 shadow-1 bg-grouped-table mt2 flex flex-column flex-row-ns items-start justify-between">
|
||||
<div class="flex flex-column items-start mr5 w-100 w-50-m w-two-thirds-l">
|
||||
<GhFormGroup @errors={{this.tag.errors}} @hasValidated={{this.tag.hasValidated}} @property="metaTitle">
|
||||
<label for="meta-title">Meta Title</label>
|
||||
<GhTextInput
|
||||
@id="meta-title"
|
||||
@name="metaTitle"
|
||||
@placeholder={{this.scratchTag.name}}
|
||||
@tabindex="4"
|
||||
@value={{this.scratchTag.metaTitle}}
|
||||
@focus-out={{action "setProperty" "metaTitle" this.scratchTag.metaTitle}}
|
||||
/>
|
||||
<GhErrorMessage @errors={{this.tag.errors}} @property="metaTitle" />
|
||||
<p>Recommended: <b>70</b> characters. You’ve used {{gh-count-down-characters this.scratchTag.metaTitle 70}}</p>
|
||||
</GhFormGroup>
|
||||
|
||||
<GhFormGroup @errors={{this.tag.errors}} @hasValidated={{this.tag.hasValidated}} @property="metaDescription">
|
||||
<label for="meta-description">Meta Description</label>
|
||||
<GhTextarea
|
||||
@id="meta-description"
|
||||
@name="metaDescription"
|
||||
@class="gh-tag-details-textarea"
|
||||
@placeholder={{this.scratchTag.description}}
|
||||
@tabindex="5"
|
||||
@value={{this.scratchTag.metaDescription}}
|
||||
@focus-out={{action "setProperty" "metaDescription" this.scratchTag.metaDescription}}
|
||||
/>
|
||||
<GhErrorMessage @errors={{this.tag.errors}} @property="metaDescription" />
|
||||
<p>Recommended: <b>156</b> characters. You’ve used {{gh-count-down-characters this.scratchTag.metaDescription 156}}</p>
|
||||
</GhFormGroup>
|
||||
</div>
|
||||
<div class="w-100 w-50-m w-third-l">
|
||||
<div class="form-group">
|
||||
<label>Search Engine Result Preview</label>
|
||||
<div class="seo-preview">
|
||||
<div class="seo-preview-title">{{this.seoTitle}}</div>
|
||||
<div class="seo-preview-link">{{this.seoURL}}</div>
|
||||
<div class="seo-preview-description">{{this.seoDescription}}</div>
|
||||
<div class="flex flex-column br3 shadow-1 bg-grouped-table mt2">
|
||||
<section class="bb b--whitegrey pa5">
|
||||
<div class="flex justify-between">
|
||||
<div>
|
||||
<h4 class="gh-setting-title">Metadata</h4>
|
||||
<p class="gh-setting-desc pa0 ma0">Extra content for search engines.</p>
|
||||
</div>
|
||||
<div>
|
||||
<button type="button" class="gh-btn" {{action (toggle "metadataOpen" this)}}><span>{{if this.metadataOpen "Close" "Expand"}}</span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{#liquid-if this.metadataOpen}}
|
||||
<div class="mt6 flex flex-column flex-row-ns items-start justify-between">
|
||||
<div class="flex flex-column items-start mr5 w-100 w-50-m w-two-thirds-l">
|
||||
<GhFormGroup @errors={{this.tag.errors}} @hasValidated={{this.tag.hasValidated}} @property="metaTitle">
|
||||
<label for="meta-title">Meta Title</label>
|
||||
<GhTextInput
|
||||
@id="meta-title"
|
||||
@name="metaTitle"
|
||||
@placeholder={{this.scratchTag.name}}
|
||||
@tabindex="4"
|
||||
@value={{this.scratchTag.metaTitle}}
|
||||
@focus-out={{action "setProperty" "metaTitle" this.scratchTag.metaTitle}}
|
||||
/>
|
||||
<GhErrorMessage @errors={{this.tag.errors}} @property="metaTitle" />
|
||||
<p>Recommended: <b>70</b> characters. You’ve used {{gh-count-down-characters this.scratchTag.metaTitle 70}}</p>
|
||||
</GhFormGroup>
|
||||
|
||||
<GhFormGroup @errors={{this.tag.errors}} @hasValidated={{this.tag.hasValidated}} @property="metaDescription">
|
||||
<label for="meta-description">Meta Description</label>
|
||||
<GhTextarea
|
||||
@id="meta-description"
|
||||
@name="metaDescription"
|
||||
@class="gh-tag-details-textarea"
|
||||
@placeholder={{this.scratchTag.description}}
|
||||
@tabindex="5"
|
||||
@value={{this.scratchTag.metaDescription}}
|
||||
@focus-out={{action "setProperty" "metaDescription" this.scratchTag.metaDescription}}
|
||||
/>
|
||||
<GhErrorMessage @errors={{this.tag.errors}} @property="metaDescription" />
|
||||
<p>Recommended: <b>156</b> characters. You’ve used {{gh-count-down-characters this.scratchTag.metaDescription 156}}</p>
|
||||
</GhFormGroup>
|
||||
<GhFormGroup @errors={{this.tag.errors}} @hasValidated={{this.tag.hasValidated}} @property="canonicalUrl">
|
||||
<label for="canonical-url">Canonical URL</label>
|
||||
<GhTextInput
|
||||
@id="canonical-url"
|
||||
@name="canonicalUrl"
|
||||
@tabindex="4"
|
||||
@value={{this.scratchTag.canonicalUrl}}
|
||||
@focus-out={{action "validateCanonicalUrl"}}
|
||||
/>
|
||||
<GhErrorMessage @errors={{this.tag.errors}} @property="canonicalUrl" />
|
||||
</GhFormGroup>
|
||||
</div>
|
||||
<div class="w-100 w-50-m w-third-l">
|
||||
<div class="form-group">
|
||||
<label>Search Engine Result Preview</label>
|
||||
<div class="seo-preview">
|
||||
<div class="seo-preview-title">{{this.seoTitle}}</div>2.1
|
||||
<div class="seo-preview-link">{{this.seoURL}}</div>
|
||||
<div class="seo-preview-description">{{this.seoDescription}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/liquid-if}}
|
||||
</section>
|
||||
<section class="bb b--whitegrey pa5">
|
||||
<div class="flex justify-between">
|
||||
<div>
|
||||
<h4 class="gh-setting-title">Twitter Card</h4>
|
||||
<p class="gh-setting-desc pa0 ma0">Customised structured data for Twitter.</p>
|
||||
</div>
|
||||
<div>
|
||||
<button type="button" class="gh-btn" {{action (toggle "twitterMetadataOpen" this)}}><span>{{if this.twitterMetadataOpen "Close" "Expand"}}</span></button>
|
||||
</div>
|
||||
</div>
|
||||
{{#liquid-if this.twitterMetadataOpen}}
|
||||
<div class="mt6 flex flex-column flex-row-ns items-start justify-between">
|
||||
<div class="flex flex-column items-start mr5 w-100 w-50-m w-two-thirds-l">
|
||||
<GhFormGroup @errors={{this.tag.errors}} @hasValidated={{this.tag.hasValidated}} @property="twitterImage">
|
||||
<label for="twitter-image">Twitter image</label>
|
||||
<GhImageUploaderWithPreview
|
||||
@image={{this.tag.twitterImage}}
|
||||
@text="Add Twitter image"
|
||||
@class="gh-tag-image-uploader"
|
||||
@allowUnsplash={{true}}
|
||||
@update={{action "setTwitterImage"}}
|
||||
@remove={{action "clearTwitterImage"}}
|
||||
/>
|
||||
</GhFormGroup>
|
||||
<GhFormGroup @errors={{this.tag.errors}} @hasValidated={{this.tag.hasValidated}} @property="twitterTitle">
|
||||
<label for="twitter-title">Twitter title</label>
|
||||
<GhTextInput
|
||||
@id="twitter-title"
|
||||
@name="twitterTitle"
|
||||
@placeholder={{this.scratchTag.name}}
|
||||
@tabindex="4"
|
||||
@value={{this.scratchTag.twitterTitle}}
|
||||
@focus-out={{action "setProperty" "twitterTitle" this.scratchTag.twitterTitle}}
|
||||
/>
|
||||
<GhErrorMessage @errors={{this.tag.errors}} @property="twitterTitle" />
|
||||
<p>Recommended: <b>70</b> characters. You’ve used {{gh-count-down-characters this.scratchTag.twitterTitle 70}}</p>
|
||||
</GhFormGroup>
|
||||
|
||||
<GhFormGroup @errors={{this.tag.errors}} @hasValidated={{this.tag.hasValidated}} @property="twitterDesctiption">
|
||||
<label for="twitter-description">Twitter Description</label>
|
||||
<GhTextarea
|
||||
@id="twitter-description"
|
||||
@name="twitterDescription"
|
||||
@class="gh-tag-details-textarea"
|
||||
@placeholder={{this.scratchTag.description}}
|
||||
@tabindex="5"
|
||||
@value={{this.scratchTag.twitterDescription}}
|
||||
@focus-out={{action "setProperty" "twitterDescription" this.scratchTag.twitterDescription}}
|
||||
/>
|
||||
<GhErrorMessage @errors={{this.tag.errors}} @property="twitterDescription" />
|
||||
<p>Recommended: <b>156</b> characters. You’ve used {{gh-count-down-characters this.scratchTag.twitterDescription 156}}</p>
|
||||
</GhFormGroup>
|
||||
</div>
|
||||
<div class="w-100 w-50-m w-third-l">
|
||||
<div class="form-group">
|
||||
<label>Preview</label>
|
||||
<div class="gh-twitter-preview">
|
||||
{{#if this.twitterImage}}
|
||||
<div class="gh-twitter-preview-image" style={{background-image-style this.twitterImage}}></div>
|
||||
{{/if}}
|
||||
<div class="gh-twitter-preview-content">
|
||||
<div class="gh-twitter-preview-title">{{this.twitterTitle}}</div>
|
||||
<div class="gh-twitter-preview-description">{{truncate this.twitterDescription 155}}</div>
|
||||
<div class="gh-twitter-preview-footer">
|
||||
<div class="gh-twitter-preview-footer-left">
|
||||
{{this.config.blogDomain}}
|
||||
</div>
|
||||
<div class="gh-twitter-preview-footer-right">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/liquid-if}}
|
||||
</section>
|
||||
<section class="bb b--whitegrey pa5">
|
||||
<div class="flex justify-between">
|
||||
<div>
|
||||
<h4 class="gh-setting-title">Facebook card</h4>
|
||||
<p class="gh-setting-desc pa0 ma0">Customise Open Graph data.</p>
|
||||
</div>
|
||||
<div>
|
||||
<button type="button" class="gh-btn" {{action (toggle "facebookMetadataOpen" this)}}><span>{{if this.facebookMetadataOpen "Close" "Expand"}}</span></button>
|
||||
</div>
|
||||
</div>
|
||||
{{#liquid-if this.facebookMetadataOpen}}
|
||||
<div class="mt6 flex flex-column flex-row-ns items-start justify-between">
|
||||
<div class="flex flex-column items-start mr5 w-100 w-50-m w-two-thirds-l">
|
||||
<GhFormGroup @errors={{this.tag.errors}} @hasValidated={{this.tag.hasValidated}} @property="ogImage">
|
||||
<label for="og-image">Facebook image</label>
|
||||
<GhImageUploaderWithPreview
|
||||
@image={{this.tag.ogImage}}
|
||||
@text="Add Facebook image"
|
||||
@class="gh-tag-image-uploader"
|
||||
@allowUnsplash={{true}}
|
||||
@update={{action "setOgImage"}}
|
||||
@remove={{action "clearOgImage"}}
|
||||
/>
|
||||
</GhFormGroup>
|
||||
<GhFormGroup @errors={{this.tag.errors}} @hasValidated={{this.tag.hasValidated}} @property="metaTitle">
|
||||
<label for="og-title">Facebook Title</label>
|
||||
<GhTextInput
|
||||
@id="og-title"
|
||||
@name="ogTitle"
|
||||
@placeholder={{this.scratchTag.name}}
|
||||
@tabindex="4"
|
||||
@value={{this.scratchTag.ogTitle}}
|
||||
@focus-out={{action "setProperty" "ogTitle" this.scratchTag.ogTitle}}
|
||||
/>
|
||||
<GhErrorMessage @errors={{this.tag.errors}} @property="ogTitle" />
|
||||
<p>Recommended: <b>70</b> characters. You’ve used {{gh-count-down-characters this.scratchTag.ogTitle 70}}</p>
|
||||
</GhFormGroup>
|
||||
|
||||
<GhFormGroup @errors={{this.tag.errors}} @hasValidated={{this.tag.hasValidated}} @property="ogDescription">
|
||||
<label for="og-description">Facebook Description</label>
|
||||
<GhTextarea
|
||||
@id="og-description"
|
||||
@name="ogDescription"
|
||||
@class="gh-tag-details-textarea"
|
||||
@placeholder={{this.scratchTag.description}}
|
||||
@tabindex="5"
|
||||
@value={{this.scratchTag.ogDescription}}
|
||||
@focus-out={{action "setProperty" "ogDescription" this.scratchTag.ogDescription}}
|
||||
/>
|
||||
<GhErrorMessage @errors={{this.tag.errors}} @property="ogDescription" />
|
||||
<p>Recommended: <b>156</b> characters. You’ve used {{gh-count-down-characters this.scratchTag.ogDescription 156}}</p>
|
||||
</GhFormGroup>
|
||||
</div>
|
||||
<div class="w-100 w-50-m w-third-l">
|
||||
<div class="form-group">
|
||||
<label>Preview</label>
|
||||
<div class="gh-og-preview">
|
||||
{{#if this.facebookImage}}
|
||||
<div class="gh-og-preview-image" style={{background-image-style this.facebookImage}}></div>
|
||||
{{/if}}
|
||||
<div class="gh-og-preview-content">
|
||||
<div class="gh-og-preview-title">{{truncate this.facebookTitle 88}}</div>
|
||||
<div class="gh-og-preview-description">{{truncate this.facebookDescription 300}}</div>
|
||||
<div class="gh-og-preview-footer">
|
||||
<div class="gh-og-preview-footer-left">
|
||||
{{this.config.blogDomain}} <span class="gh-og-preview-footer-left-divider">|</span> by <span class="gh-og-preview-footer-author">{{author-names this.post.authors}}</span>
|
||||
</div>
|
||||
<div class="gh-og-preview-footer-right">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/liquid-if}}
|
||||
</section>
|
||||
<section class="bb b--whitegrey pa5">
|
||||
<div class="flex justify-between">
|
||||
<div>
|
||||
<h4 class="gh-setting-title">Code injection</h4>
|
||||
<p class="gh-setting-desc pa0 ma0">Add styles/scripts to the header and footer.</p>
|
||||
</div>
|
||||
<div>
|
||||
<button type="button" class="gh-btn" {{action (toggle "codeInjectionOpen" this)}}><span>{{if this.codeInjectionOpen "Close" "Expand"}}</span></button>
|
||||
</div>
|
||||
</div>
|
||||
{{#liquid-if this.codeInjectionOpen}}
|
||||
<div class="mt6 flex flex-column flex-row-ns flex-wrap items-start justify-between">
|
||||
<GhFormGroup @class="settings-code" @errors={{this.tag.errors}} @hasValidated={{this.tag.hasValidated}} @property="codeinjectionHead">
|
||||
<label for="codeinjection-head" class="gh-tag-setting-codeheader">Tag header <code class="fw4 ml1">\{{ghost_head}}</code></label>
|
||||
<GhCmEditor @value={{this.codeinjectionHeadScratch}}
|
||||
@id="tag-setting-codeinjection-head"
|
||||
@class="gh-tag-setting-codeinjection"
|
||||
@name="tag-setting-codeinjection-head"
|
||||
@focusOut={{action "setProperty" "codeinjectionHead" this.codeinjectionHeadScratch}}
|
||||
@stopEnterKeyDownPropagation="true"
|
||||
@update={{action (mut this.codeinjectionHeadScratch)}}
|
||||
/>
|
||||
<GhErrorMessage @errors={{this.tag.errors}} @property="codeinjectionHead"/>
|
||||
</GhFormGroup>
|
||||
|
||||
<GhFormGroup @class="settings-code" @errors={{this.tag.errors}} @hasValidated={{this.tag.hasValidated}} @property="codeinjectionFoot">
|
||||
<label for="codeinjection-foot"class="gh-tag-setting-codeheader">Tag footer <code class="fw4 ml1">\{{ghost_foot}}</code></label>
|
||||
<GhCmEditor @value={{this.codeinjectionfootScratch}}
|
||||
@id="tag-setting-codeinjection-foot"
|
||||
@class="gh-tag-setting-codeinjection"
|
||||
@name="tag-setting-codeinjection-foot"
|
||||
@focusOut={{action "setProperty" "codeinjectionFoot" this.codeinjectionFootScratch}}
|
||||
@stopEnterKeyDownPropagation="true"
|
||||
@update={{action (mut this.codeinjectionFootScratch)}}
|
||||
/>
|
||||
<GhErrorMessage @errors={{this.tag.errors}} @property="codeinjectionFoot"/>
|
||||
</GhFormGroup>
|
||||
</div>
|
||||
{{/liquid-if}}
|
||||
</section>
|
||||
</div>
|
|
@ -2,6 +2,8 @@ import Component from '@ember/component';
|
|||
import Ember from 'ember';
|
||||
import {computed} from '@ember/object';
|
||||
import {htmlSafe} from '@ember/string';
|
||||
import {or} from '@ember/object/computed';
|
||||
import {run} from '@ember/runloop';
|
||||
import {inject as service} from '@ember/service';
|
||||
|
||||
const {Handlebars} = Ember;
|
||||
|
@ -16,6 +18,27 @@ export default Component.extend({
|
|||
// Allowed actions
|
||||
setProperty: () => {},
|
||||
|
||||
twitterTitle: or('scratchTag.twitterTitle', 'seoTitle'),
|
||||
twitterDescription: or('scratchTag.twitterDescription', 'seoDescription'),
|
||||
twitterImage: or('tag.twitterImage', 'tag.featureImage'),
|
||||
|
||||
facebookTitle: or('scratchTag.ogTitle', 'seoTitle'),
|
||||
facebookDescription: or('scratchTag.ogDescription', 'seoDescription'),
|
||||
facebookImage: or('tag.ogImage', 'tag.featureImage'),
|
||||
|
||||
accentColor: computed('tag.accentColor', function () {
|
||||
let color = this.get('tag.accentColor');
|
||||
if (color && color[0] === '#') {
|
||||
return color.slice(1);
|
||||
}
|
||||
return color;
|
||||
}),
|
||||
|
||||
accentColorBackgroundStyle: computed('tag.accentColor', function () {
|
||||
let color = this.get('tag.accentColor') || '#ffffff';
|
||||
return htmlSafe(`background-color: ${color}`);
|
||||
}),
|
||||
|
||||
title: computed('tag.isNew', function () {
|
||||
if (this.get('tag.isNew')) {
|
||||
return 'New tag';
|
||||
|
@ -24,10 +47,8 @@ export default Component.extend({
|
|||
}
|
||||
}),
|
||||
|
||||
seoTitle: computed('scratchTag.{title,metaTitle}', function () {
|
||||
let metaTitle = this.scratchTag.metaTitle || '';
|
||||
|
||||
metaTitle = metaTitle.length > 0 ? metaTitle : this.scratchTag.title;
|
||||
seoTitle: computed('scratchTag.{name,metaTitle}', function () {
|
||||
let metaTitle = this.scratchTag.metaTitle || this.scratchTag.name;
|
||||
|
||||
if (metaTitle && metaTitle.length > 70) {
|
||||
metaTitle = metaTitle.substring(0, 70).trim();
|
||||
|
@ -38,14 +59,15 @@ export default Component.extend({
|
|||
return metaTitle;
|
||||
}),
|
||||
|
||||
seoURL: computed('scratchTag.slug', function () {
|
||||
seoURL: computed('scratchTag.{canonicalUrl,slug}', function () {
|
||||
let blogUrl = this.get('config.blogUrl');
|
||||
let seoSlug = this.scratchTag.slug || '';
|
||||
|
||||
let seoURL = `${blogUrl}/tag/${seoSlug}`;
|
||||
let seoURL = this.scratchTag.canonicalUrl || `${blogUrl}/tag/${seoSlug}`;
|
||||
|
||||
// only append a slash to the URL if the slug exists
|
||||
if (seoSlug) {
|
||||
|
||||
if (!seoURL.endsWith('/')) {
|
||||
seoURL += '/';
|
||||
}
|
||||
|
||||
|
@ -77,12 +99,91 @@ export default Component.extend({
|
|||
this.setProperty(property, value);
|
||||
},
|
||||
|
||||
setTwitterImage(image) {
|
||||
this.setProperty('twitterImage', image);
|
||||
},
|
||||
|
||||
clearTwitterImage() {
|
||||
this.setProperty('twitterImage', '');
|
||||
},
|
||||
|
||||
setOgImage(image) {
|
||||
this.setProperty('ogImage', image);
|
||||
},
|
||||
|
||||
clearOgImage() {
|
||||
this.setProperty('ogImage', '');
|
||||
},
|
||||
|
||||
setCoverImage(image) {
|
||||
this.setProperty('featureImage', image);
|
||||
},
|
||||
|
||||
clearCoverImage() {
|
||||
this.setProperty('featureImage', '');
|
||||
},
|
||||
|
||||
validateCanonicalUrl() {
|
||||
let newUrl = this.get('scratchTag.canonicalUrl');
|
||||
let oldUrl = this.get('tag.canonicalUrl');
|
||||
let errMessage = '';
|
||||
|
||||
this.get('tag.errors').remove('canonicalUrl');
|
||||
this.get('tag.hasValidated').removeObject('canonicalUrl');
|
||||
|
||||
if (newUrl === '') {
|
||||
this.setProperty('canonicalUrl', '');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!newUrl) {
|
||||
newUrl = oldUrl;
|
||||
}
|
||||
|
||||
try {
|
||||
new URL(newUrl);
|
||||
this.setProperty('canonicalUrl', '');
|
||||
run.schedule('afterRender', this, function () {
|
||||
this.setProperty('canonicalUrl', newUrl);
|
||||
});
|
||||
} catch (err) {
|
||||
errMessage = 'The url should be a valid url';
|
||||
this.get('tag.errors').add('canonicalUrl', errMessage);
|
||||
this.get('tag.hasValidated').pushObject('canonicalUrl');
|
||||
}
|
||||
},
|
||||
|
||||
validateAccentColor() {
|
||||
let newColor = this.get('accentColor');
|
||||
let oldColor = this.get('tag.accentColor');
|
||||
let errMessage = '';
|
||||
|
||||
this.get('tag.errors').remove('accentColor');
|
||||
this.get('tag.hasValidated').removeObject('accentColor');
|
||||
|
||||
if (newColor === '') {
|
||||
this.setProperty('accentColor', '');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!newColor) {
|
||||
newColor = oldColor;
|
||||
}
|
||||
|
||||
if (newColor[0] !== '#') {
|
||||
newColor = `#${newColor}`;
|
||||
}
|
||||
|
||||
if (newColor.match(/#[0-9A-Fa-f]{6}$/)) {
|
||||
this.setProperty('accentColor', '');
|
||||
run.schedule('afterRender', this, function () {
|
||||
this.setProperty('accentColor', newColor);
|
||||
});
|
||||
} else {
|
||||
errMessage = 'The color should be in valid hex format';
|
||||
this.get('tag.errors').add('accentColor', errMessage);
|
||||
this.get('tag.hasValidated').pushObject('accentColor');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -88,6 +88,9 @@ export default Controller.extend({
|
|||
tag.setProperties(scratchProps);
|
||||
|
||||
try {
|
||||
if (tag.get('errors').length !== 0) {
|
||||
return;
|
||||
}
|
||||
yield tag.save();
|
||||
|
||||
// replace 'new' route with 'tag' route
|
||||
|
|
|
@ -12,6 +12,16 @@ export default Model.extend(ValidationEngine, {
|
|||
parent: attr('string'), // unused
|
||||
metaTitle: attr('string'),
|
||||
metaDescription: attr('string'),
|
||||
twitterImage: attr('string'),
|
||||
twitterTitle: attr('string'),
|
||||
twitterDescription: attr('string'),
|
||||
ogImage: attr('string'),
|
||||
ogTitle: attr('string'),
|
||||
ogDescription: attr('string'),
|
||||
codeinjectionHead: attr('string'),
|
||||
codeinjectionFoot: attr('string'),
|
||||
canonicalUrl: attr('string'),
|
||||
accentColor: attr('string'),
|
||||
featureImage: attr('string'),
|
||||
visibility: attr('string', {defaultValue: 'public'}),
|
||||
createdAtUTC: attr('moment-utc'),
|
||||
|
|
|
@ -813,8 +813,8 @@ p.theme-validation-details {
|
|||
.input-color:after {
|
||||
content: "#";
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
left: 40px;
|
||||
top: 9px;
|
||||
left: 43px;
|
||||
color: var(--midlightgrey);
|
||||
font-family: "Consolas", monaco, monospace;
|
||||
font-size: 13px;
|
||||
|
@ -825,9 +825,9 @@ p.theme-validation-details {
|
|||
}
|
||||
|
||||
.input-color input {
|
||||
padding-left: 48px;
|
||||
width: 110px;
|
||||
height: 33px;
|
||||
padding-left: 52px;
|
||||
width: 112px;
|
||||
height: 38px;
|
||||
padding-right: 8px;
|
||||
font-family: "Consolas", monaco, monospace;
|
||||
font-size: 13px;
|
||||
|
@ -837,8 +837,8 @@ p.theme-validation-details {
|
|||
position: absolute;
|
||||
top: 1px;
|
||||
left: 1px;
|
||||
width: 31px;
|
||||
height: 31px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
display: inline-block;
|
||||
background-color: var(--lightgrey);
|
||||
border-top-left-radius: 4px;
|
||||
|
@ -850,8 +850,8 @@ p.theme-validation-details {
|
|||
.input-color input:focus + .color-box {
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
width: 30px;
|
||||
height: 29px;
|
||||
width: 35px;
|
||||
height: 34px;
|
||||
border-top-left-radius: 3px;
|
||||
border-bottom-left-radius: 3px;
|
||||
}
|
|
@ -138,4 +138,39 @@ textarea.gh-tag-details-textarea {
|
|||
margin: 4px 0 0;
|
||||
border: 1px solid var(--lightgrey);
|
||||
min-height: 147px;
|
||||
}
|
||||
|
||||
.gh-tag-setting-codeinjection .CodeMirror {
|
||||
padding: 0 !important;
|
||||
min-height: 240px;
|
||||
}
|
||||
|
||||
.gh-tag-setting-codeinjection .CodeMirror-scroll {
|
||||
min-height: 240px;
|
||||
}
|
||||
|
||||
label.gh-tag-setting-codeheader {
|
||||
font-size: 1.3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.gh-tag-settings-multiprop {
|
||||
display: flex;
|
||||
max-width: 620px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.gh-tag-settings-colorcontainer {
|
||||
flex-basis: 112px;
|
||||
}
|
||||
|
||||
@media (max-width: 1080px) {
|
||||
.gh-tag-settings-multiprop {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.gh-tag-settings-colorcontainer {
|
||||
flex-basis: unset;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue