Skip to content

Commit

Permalink
v3 - validate page slug chars (#500)
Browse files Browse the repository at this point in the history
* fix(slug): stronger slug field generation and validation

* refactor(slug): combine blog-, legal- and titleSlug into pageTitleSlug

* refactor(slug): rename pageTitleSlug -> titleSlug
  • Loading branch information
mathiazom authored Aug 23, 2024
1 parent 2aa0a23 commit 6be8e37
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 14 deletions.
4 changes: 2 additions & 2 deletions studio/schemas/documents/legalDocuments.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { defineField } from "sanity";
import { richText, title } from "../fields/text";
import { legalSlug } from "../schemaTypes/slug";
import { titleSlug } from "../schemaTypes/slug";

export const legalDocumentID = "legalDocuments";

const legalDocument = defineField({
name: legalDocumentID,
type: "document",
title: "Legal Document",
fields: [title, legalSlug, richText],
fields: [title, titleSlug, richText],
preview: {
select: {
title: "basicTitle",
Expand Down
4 changes: 2 additions & 2 deletions studio/schemas/documents/post.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { defineField, defineType } from "sanity";
import { format, parseISO } from "date-fns";
import { blogSlug } from "../schemaTypes/slug";
import { titleSlug } from "../schemaTypes/slug";
import { richText, title } from "../fields/text";
import CategorySelector from "studio/components/CategorySelect";
import image from "../fields/media";
Expand All @@ -13,7 +13,7 @@ const posts = defineType({
title: "Posts",
fields: [
title,
blogSlug,
titleSlug,
defineField({
name: "date",
title: "Publish Date",
Expand Down
4 changes: 2 additions & 2 deletions studio/schemas/documents/salaryAndBenefits.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { defineField, defineType } from "sanity";
import { pageSlug } from "../schemaTypes/slug";
import { titleSlug } from "../schemaTypes/slug";
import seo from "../objects/seo";
import { title } from "../fields/text";
import { benefitId } from "./benefit";
Expand All @@ -12,7 +12,7 @@ const salaryAndBenefits = defineType({
title: "Salary and Benefits",
fields: [
title,
pageSlug,
titleSlug,
seo,
defineField({
name: "showSalaryCalculator",
Expand Down
32 changes: 24 additions & 8 deletions studio/schemas/schemaTypes/slug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { defineField, SlugValidationContext } from "sanity";

async function isSlugUniqueAcrossAllDocuments(
slug: string,
context: SlugValidationContext
context: SlugValidationContext,
) {
const { document, getClient } = context;
const client = getClient({ apiVersion: "2022-12-07" });
Expand All @@ -19,26 +19,42 @@ async function isSlugUniqueAcrossAllDocuments(
return result;
}

const SLUG_MAX_LENGTH = 200;

function createSlugField(source: string) {
return defineField({
type: "slug",
name: "slug",
title: "URL Path (slug)",
description:
"Enter a unique URL path for the page. This path will be used in the website's address bar. A URL path, also known as a slug, is a URL-friendly version of the page title, used to create a human-readable and search engine optimized URL for the content.",
"Enter a unique URL path for the page. This path will be used in the website's address bar. A URL path, also known as a slug, is a URL-friendly version of the page title, used to create a human-readable and search engine optimized URL for the content. Legal characters include latin letters, digits, hyphen (-), underscore (_), full stop (.) and tilde (~)",
options: {
source,
maxLength: 200,
maxLength: SLUG_MAX_LENGTH,
slugify: (input) =>
input.toLowerCase().replace(/\s+/g, "-").slice(0, 200),
input
.toLowerCase()
// replace æøå according to https://sprakradet.no/spraksporsmal-og-svar/ae-o-og-a-i-internasjonal-sammenheng/
.replace(/[æ,å]/g, "a")
.replace(/ø/g, "o")
.replace(/[^a-zA-Z0-9-_.~\s]/g, "") // remove non-whitespace URL-unsafe chars (section 2.3 in https://www.ietf.org/rfc/rfc3986.txt)
.trim()
.replace(/\s+/g, "-")
.slice(0, SLUG_MAX_LENGTH),
isUnique: isSlugUniqueAcrossAllDocuments,
},
validation: (Rule) => Rule.required(),
validation: (Rule) =>
Rule.required().custom((value) => {
if (value?.current === undefined) return true;
return (
encodeURIComponent(value.current) === value.current ||
"Slug can only consist of latin letters, digits, hyphen (-), underscore (_), full stop (.) and tilde (~)"
);
}),
});
}

const pageSlug = createSlugField("page");
const blogSlug = createSlugField("basicTitle");
const legalSlug = createSlugField("basicTitle");
const titleSlug = createSlugField("basicTitle");

export { pageSlug, blogSlug, legalSlug };
export { pageSlug, titleSlug };

0 comments on commit 6be8e37

Please sign in to comment.