diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-edit-page/content/dot-edit-content.component.html b/core-web/apps/dotcms-ui/src/app/portlets/dot-edit-page/content/dot-edit-content.component.html index 213c1148cafe..aebb8f8b6c44 100644 --- a/core-web/apps/dotcms-ui/src/app/portlets/dot-edit-page/content/dot-edit-content.component.html +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-edit-page/content/dot-edit-content.component.html @@ -40,15 +40,22 @@ *dotShowHideFeature="featureFlagSeo; alternate: disabledComponent" [ngTemplateOutlet]="enabledComponent"> -
og:description
meta tag found!'
);
- expect(value[0].items[1].message).toEqual(
+ expect(value[5].items[1].message).toEqual(
'og:description
meta tag found, but has fewer than 55 characters of content.'
);
done();
});
});
- it('should that got more than one og:title error', (done) => {
+ it('should get more than one og:title error', (done) => {
const ogMetaTitle = document.createElement('meta');
ogMetaTitle.setAttribute('property', 'og:title');
ogMetaTitle.setAttribute('content', 'Costa Rica Special Offer');
@@ -239,12 +238,32 @@ describe('DotSetMetaTagsService', () => {
service.getMetaTagsResults(testDoc).subscribe((value) => {
expect(value[2].items[0].message).toEqual(
- 'more than 1 og:title
metatag found!'
+ 'more than 1 og:title
meta tag found!'
);
expect(value[2].items[1].message).toEqual(
- 'title metatag found, but has fewer than 30 characters of content.'
+ 'title meta tag found, but has fewer than 30 characters of content.'
);
done();
});
});
+
+ it('should get more than description error', (done) => {
+ const description = document.createElement('meta');
+ description.setAttribute('name', 'description');
+ description.setAttribute('content', 'Costa Rica Special Offer');
+
+ testDoc.head.appendChild(description);
+
+ service.getMetaTagsResults(testDoc).subscribe((value) => {
+ expect(value[0].items[0].message).toEqual('More than 1 Meta Description found!');
+ done();
+ });
+ });
+
+ it('should get description found', (done) => {
+ service.getMetaTagsResults(testDoc).subscribe((value) => {
+ expect(value[0].items[0].message).toEqual('Meta Description found!');
+ done();
+ });
+ });
});
diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-edit-page/content/services/html/dot-seo-meta-tags.service.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-edit-page/content/services/html/dot-seo-meta-tags.service.ts
index c0a27ef564d9..4ff0d083890f 100644
--- a/core-web/apps/dotcms-ui/src/app/portlets/dot-edit-page/content/services/html/dot-seo-meta-tags.service.ts
+++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-edit-page/content/services/html/dot-seo-meta-tags.service.ts
@@ -60,6 +60,7 @@ export class DotSeoMetaTagsService {
const descriptionOgElements = pageDocument.querySelectorAll(
'meta[property="og:description"]'
);
+ const descriptionElements = pageDocument.querySelectorAll('meta[name="description"]');
const twitterCardElements = pageDocument.querySelectorAll('meta[name="twitter:card"]');
const twitterTitleElements = pageDocument.querySelectorAll('meta[name="twitter:title"]');
const twitterImageElements = pageDocument.querySelectorAll('meta[name="twitter:image"]');
@@ -78,6 +79,7 @@ export class DotSeoMetaTagsService {
metaTagsObject['twitterDescriptionElements'] = twitterDescriptionElements;
metaTagsObject['twitterImageElements'] = twitterImageElements;
metaTagsObject['descriptionOgElements'] = descriptionOgElements;
+ metaTagsObject['descriptionElements'] = descriptionElements;
return metaTagsObject;
}
@@ -105,8 +107,7 @@ export class DotSeoMetaTagsService {
keyIcon: keysValues.keyIcon,
keyColor: keysValues.keyColor,
items: items,
- sort: ogMap[key]?.sort,
- info: ogMap[key]?.info
+ sort: ogMap[key]?.sort
};
});
})
@@ -119,59 +120,49 @@ export class DotSeoMetaTagsService {
return {
[SEO_OPTIONS.FAVICON]: {
getItems: (metaTagsObject: SeoMetaTags) => of(this.getFaviconItems(metaTagsObject)),
- sort: 1,
- info: ''
+ sort: 1
},
[SEO_OPTIONS.DESCRIPTION]: {
getItems: (metaTagsObject: SeoMetaTags) =>
of(this.getDescriptionItems(metaTagsObject)),
- sort: 3,
- info: this.dotMessageService.get('seo.rules.description.info')
+ sort: 3
},
[SEO_OPTIONS.OG_DESCRIPTION]: {
getItems: (metaTagsObject: SeoMetaTags) =>
- of(this.getDescriptionItems(metaTagsObject)),
- sort: 4,
- info: this.dotMessageService.get('seo.rules.description.info')
+ of(this.getOgDescriptionItems(metaTagsObject)),
+ sort: 4
},
[SEO_OPTIONS.TITLE]: {
getItems: (metaTagsObject: SeoMetaTags) => of(this.getTitleItems(metaTagsObject)),
- sort: 2,
- info: this.dotMessageService.get('seo.rules.title.info')
+ sort: 2
},
[SEO_OPTIONS.OG_TITLE]: {
getItems: (metaTagsObject: SeoMetaTags) => of(this.getOgTitleItems(metaTagsObject)),
- sort: 2,
- info: this.dotMessageService.get('seo.rules.title.info')
+ sort: 2
},
[SEO_OPTIONS.OG_IMAGE]: {
getItems: (metaTagsObject: SeoMetaTags) => this.getOgImagesItems(metaTagsObject),
- sort: 6,
- info: ''
+ sort: 6
},
[SEO_OPTIONS.TWITTER_CARD]: {
getItems: (metaTagsObject: SeoMetaTags) =>
of(this.getTwitterCardItems(metaTagsObject)),
- sort: 2,
- info: ''
+ sort: 2
},
[SEO_OPTIONS.TWITTER_TITLE]: {
getItems: (metaTagsObject: SeoMetaTags) =>
of(this.getTwitterTitleItems(metaTagsObject)),
- sort: 1,
- info: ''
+ sort: 1
},
[SEO_OPTIONS.TWITTER_DESCRIPTION]: {
getItems: (metaTagsObject: SeoMetaTags) =>
of(this.getTwitterDescriptionItems(metaTagsObject)),
- sort: 3,
- info: ''
+ sort: 3
},
[SEO_OPTIONS.TWITTER_IMAGE]: {
getItems: (metaTagsObject: SeoMetaTags) =>
this.getTwitterImageItems(metaTagsObject),
- sort: 4,
- info: ''
+ sort: 4
}
};
}
@@ -199,7 +190,9 @@ export class DotSeoMetaTagsService {
const faviconElements = metaTagsObject['faviconElements'];
if (faviconElements.length === 0) {
- items.push();
+ items.push(
+ this.getErrorItem(this.dotMessageService.get('seo.rules.favicon.not.found'))
+ );
}
if (faviconElements.length > SEO_LIMITS.MAX_FAVICONS) {
@@ -235,10 +228,9 @@ export class DotSeoMetaTagsService {
};
}
- private getDescriptionItems(metaTagsObject: SeoMetaTags): SeoRulesResult[] {
+ private getOgDescriptionItems(metaTagsObject: SeoMetaTags): SeoRulesResult[] {
const result: SeoRulesResult[] = [];
const ogDescription = metaTagsObject['og:description'];
- const description = metaTagsObject['description'];
const descriptionOgElements = metaTagsObject['descriptionOgElements'];
if (descriptionOgElements?.length > 1) {
@@ -249,7 +241,7 @@ export class DotSeoMetaTagsService {
);
}
- if (!ogDescription && description) {
+ if (!ogDescription || descriptionOgElements.length === 0) {
result.push(
this.getErrorItem(this.dotMessageService.get('seo.rules.og-description.not.found'))
);
@@ -288,15 +280,71 @@ export class DotSeoMetaTagsService {
return result;
}
+ private getDescriptionItems(metaTagsObject: SeoMetaTags): SeoRulesResult[] {
+ const result: SeoRulesResult[] = [];
+ const description = metaTagsObject['description'];
+ const descriptionElements = metaTagsObject['descriptionElements'];
+
+ if (descriptionElements?.length > 1) {
+ result.push(
+ this.getErrorItem(
+ this.dotMessageService.get('seo.rules.description.more.one.found')
+ )
+ );
+ }
+
+ if (this.areAllFalsyOrEmpty([description, descriptionElements])) {
+ result.push(
+ this.getErrorItem(this.dotMessageService.get('seo.rules.description.not.found'))
+ );
+ }
+
+ if (descriptionElements.length >= 1 && this.areAllFalsyOrEmpty([description])) {
+ result.push(
+ this.getErrorItem(this.dotMessageService.get('seo.rules.description.found.empty'))
+ );
+ }
+
+ if (description?.length < SEO_LIMITS.MIN_DESCRIPTION_LENGTH) {
+ result.push(
+ this.getWarningItem(this.dotMessageService.get('seo.rules.description.less'))
+ );
+ }
+
+ if (description?.length > SEO_LIMITS.MAX_DESCRIPTION_LENGTH) {
+ result.push(
+ this.getWarningItem(this.dotMessageService.get('seo.rules.description.greater'))
+ );
+ }
+
+ if (
+ description &&
+ description?.length > SEO_LIMITS.MIN_OG_DESCRIPTION_LENGTH &&
+ description?.length < SEO_LIMITS.MAX_OG_DESCRIPTION_LENGTH
+ ) {
+ result.push(
+ this.getDoneItem(this.dotMessageService.get('seo.rules.description.found'))
+ );
+ }
+
+ return result;
+ }
+
private getTitleItems(metaTagsObject: SeoMetaTags): SeoRulesResult[] {
const result: SeoRulesResult[] = [];
const title = metaTagsObject['title'];
const titleElements = metaTagsObject['titleElements'];
- if (!titleElements) {
+ if (this.areAllFalsyOrEmpty([title, titleElements])) {
result.push(this.getErrorItem(this.dotMessageService.get('seo.rules.title.not.found')));
}
+ if (titleElements?.length >= 1 && this.areAllFalsyOrEmpty([title])) {
+ result.push(
+ this.getErrorItem(this.dotMessageService.get('seo.rules.title.found.empty'))
+ );
+ }
+
if (titleElements?.length > 1) {
result.push(
this.getErrorItem(this.dotMessageService.get('seo.rules.title.more.one.found'))
@@ -325,10 +373,26 @@ export class DotSeoMetaTagsService {
private getOgTitleItems(metaTagsObject: SeoMetaTags): SeoRulesResult[] {
const result: SeoRulesResult[] = [];
const titleOgElements = metaTagsObject['titleOgElements'];
+ const titleElements = metaTagsObject['titleElements'];
const titleOg = metaTagsObject['og:title'];
+ const title = metaTagsObject['title'];
+
+ if (title && this.areAllFalsyOrEmpty([titleOg, titleOgElements])) {
+ result.push(
+ this.getErrorItem(this.dotMessageService.get('seo.rules.og-title.not.found'))
+ );
+ }
+
+ if (this.areAllFalsyOrEmpty([title, titleOg, titleElements, titleOgElements])) {
+ result.push(
+ this.getErrorItem(this.dotMessageService.get('seo.rules.og-title.not.found.title'))
+ );
+ }
- if (!titleOgElements) {
- result.push(this.getErrorItem(this.dotMessageService.get('seo.rules.image.not.found')));
+ if (titleOgElements?.length >= 1 && this.areAllFalsyOrEmpty([titleOg])) {
+ result.push(
+ this.getErrorItem(this.dotMessageService.get(' seo.rules.og-title.found.empty'))
+ );
}
if (titleOgElements?.length > 1) {
@@ -372,7 +436,7 @@ export class DotSeoMetaTagsService {
);
}
- if (!imageOgElements) {
+ if (this.areAllFalsyOrEmpty([imageOgElements, imageOg])) {
result.push(
this.getErrorItem(
this.dotMessageService.get('seo.rules.og-image.not.found')
@@ -395,32 +459,32 @@ export class DotSeoMetaTagsService {
private getTwitterCardItems(metaTagsObject: SeoMetaTags): SeoRulesResult[] {
const result: SeoRulesResult[] = [];
- const titleCardElements = metaTagsObject['twitterCardElements'];
- const titleCard = metaTagsObject['twitter:card'];
+ const twitterCardElements = metaTagsObject['twitterCardElements'];
+ const twitterCard = metaTagsObject['twitter:card'];
- if (titleCardElements.length === 0) {
+ if (this.areAllFalsyOrEmpty([twitterCard, twitterCardElements])) {
result.push(
this.getErrorItem(this.dotMessageService.get('seo.rules.twitter-card.not.found'))
);
}
- if (titleCardElements?.length > 1) {
+ if (twitterCardElements.length >= 1 && this.areAllFalsyOrEmpty([twitterCard])) {
result.push(
this.getErrorItem(
- this.dotMessageService.get('seo.rules.twitter-card.more.one.found')
+ this.dotMessageService.get('seo.rules.twitter-card.more.one.found.empty')
)
);
}
- if (titleCard && titleCard.length === 0) {
+ if (twitterCardElements?.length > 1) {
result.push(
this.getErrorItem(
- this.dotMessageService.get('seo.rules.twitter-card.more.one.found.empty')
+ this.dotMessageService.get('seo.rules.twitter-card.more.one.found')
)
);
}
- if (titleCard) {
+ if (twitterCard) {
result.push(
this.getDoneItem(this.dotMessageService.get('seo.rules.twitter-card.found'))
);
@@ -434,7 +498,7 @@ export class DotSeoMetaTagsService {
const titleCardElements = metaTagsObject['twitterTitleElements'];
const titleCard = metaTagsObject['twitter:title'];
- if (titleCardElements.length === 0) {
+ if (this.areAllFalsyOrEmpty([titleCard, titleCardElements])) {
result.push(
this.getErrorItem(
this.dotMessageService.get('seo.rules.twitter-card-title.not.found')
@@ -450,13 +514,31 @@ export class DotSeoMetaTagsService {
);
}
- if (titleCard && titleCard.length === 0) {
+ if (titleCardElements.length >= 1 && this.areAllFalsyOrEmpty([titleCard])) {
result.push(
this.getErrorItem(this.dotMessageService.get('seo.rules.twitter-card-title.empty'))
);
}
- if (titleCard) {
+ if (titleCard?.length < SEO_LIMITS.MIN_TWITTER_TITLE_LENGTH) {
+ result.push(
+ this.getWarningItem(this.dotMessageService.get('seo.rules.twitter-card.title.less'))
+ );
+ }
+
+ if (titleCard?.length > SEO_LIMITS.MAX_TWITTER_TITLE_LENGTH) {
+ result.push(
+ this.getWarningItem(
+ this.dotMessageService.get('seo.rules.twitter-card.title.greater')
+ )
+ );
+ }
+
+ if (
+ titleCard &&
+ titleCard?.length > SEO_LIMITS.MIN_TWITTER_TITLE_LENGTH &&
+ titleCard?.length < SEO_LIMITS.MAX_TWITTER_TITLE_LENGTH
+ ) {
result.push(
this.getDoneItem(this.dotMessageService.get('seo.rules.twitter-card-title.found'))
);
@@ -470,7 +552,7 @@ export class DotSeoMetaTagsService {
const twitterDescriptionElements = metaTagsObject['twitterDescriptionElements'];
const twitterDescription = metaTagsObject['twitter:description'];
- if (twitterDescriptionElements.length === 0) {
+ if (this.areAllFalsyOrEmpty([twitterDescription, twitterDescriptionElements])) {
result.push(
this.getErrorItem(
this.dotMessageService.get('seo.rules.twitter-card-description.not.found')
@@ -486,7 +568,10 @@ export class DotSeoMetaTagsService {
);
}
- if (twitterDescription && twitterDescription.length === 0) {
+ if (
+ twitterDescriptionElements.length >= 1 &&
+ this.areAllFalsyOrEmpty([twitterDescription])
+ ) {
result.push(
this.getErrorItem(
this.dotMessageService.get(
@@ -501,7 +586,7 @@ export class DotSeoMetaTagsService {
twitterDescription.length > SEO_LIMITS.MAX_TWITTER_DESCRIPTION_LENGTH
) {
result.push(
- this.getErrorItem(
+ this.getWarningItem(
this.dotMessageService.get('seo.rules.twitter-card-description.greater')
)
);
@@ -537,7 +622,7 @@ export class DotSeoMetaTagsService {
);
}
- if (twitterImageElements.length === 0) {
+ if (this.areAllFalsyOrEmpty([twitterImage, twitterImageElements])) {
result.push(
this.getErrorItem(
this.dotMessageService.get('seo.rules.twitter-image.not.found')
@@ -564,6 +649,12 @@ export class DotSeoMetaTagsService {
);
}
+ private areAllFalsyOrEmpty(values: (string | NodeListOf