decap-cms icon indicating copy to clipboard operation
decap-cms copied to clipboard

Make sluggification visible, customizable, and fail-safe

Open erquhart opened this issue 8 years ago • 57 comments

Default sluggification is simplistic, relying on a title field and performing no inference whatsoever.

With no field named "title", entries currently fail to save.

The ideal fix is to simply make slugs editable in the editor UI.

Considerations:

  • Slug editing can be switched off through config
  • If slug editing is switched off, each collection config must be validated on CMS load to ensure a slug can be derived
  • Prepopulate with the same best guess or configured slug template result that we currently use
  • Only valid characters may be entered in the slug field
  • Slugs and filenames are not always the same, so it should be possible to provide them separately somehow (see comment below for example)

erquhart avatar Jun 06 '17 21:06 erquhart

@erquhart how does the slug entry in collections affect sluggification - is it functional anymore? (I've been working under the assumption that the current behavior is that the title field is sluggified and turned into the {{slug}} part of the slug template).

Benaiah avatar Jun 06 '17 21:06 Benaiah

Yep, that's the default behavior, but date segments can be used for some light slug templating: https://github.com/netlify/netlify-cms/blob/e388ed1721576221523776bfcf25f7124b5f116c/src/backends/backend.js#L27-L44

erquhart avatar Jun 06 '17 21:06 erquhart

May happen in tandem with #180.

erquhart avatar Jun 28 '17 01:06 erquhart

We currently overwrite any existing entry when a new entry is created that has the same slug. This is probably best fixed along with this issue.

tech4him1 avatar Sep 19 '17 19:09 tech4him1

I also think we should throw an error if the slug is going to be blank.

tech4him1 avatar Sep 20 '17 00:09 tech4him1

It is a bad idea to silently overwrite entries. If a user creates multiple entries on same day and does not bother about titles, then all entries except last will be lost without any notification. This is easily the worst imaginable behaviour.

aperep avatar Sep 20 '17 19:09 aperep

I wonder if we should change or prioritize how we visualize the 'title' field to imply it's importance

t1merickson avatar Dec 17 '17 22:12 t1merickson

We're actually looking to eliminate it's importance. Right now it's critical because it's used for automated slug creation, which the user can't see or influence. This issue calls for making slug creation interactive. We could pre-fill with whatever's in a title field, but if there's no field (or the slug is blank for any other reason), the entry would fail validation and could not be saved.

erquhart avatar Dec 22 '17 22:12 erquhart

Hi! What's the status on slug editing, anyone working on a suggestion?

bjrn avatar Feb 16 '18 11:02 bjrn

@bjrn no one is working this yet, definitely open to someone taking a stab at it.

erquhart avatar Feb 22 '18 20:02 erquhart

May I point something? Some SSGs allow us to overwrite the filename based slug. This is speacially useful for Hugo multilingual files. We can have a travel.en.md file with slug: "travel" on front matter and a travel.pt.md file with slug: "viagem".

Can we avoid conflicts between the two things?

robsonsobral avatar Mar 30 '18 02:03 robsonsobral

@robsonsobral I'm not sure what you're saying Netlify CMS should do differently to accommodate for this. Can you explain further?

Update: Your comment on #1063 made it click for me, you're saying filenames and slugs shouldn't be assumed to map 1:1. This is a great point, I'll add it to the OP.

Would you have time to write up some thoughts on what setting both custom slugs and filenames could look like from your perspective?

erquhart avatar Apr 02 '18 19:04 erquhart

Actually, @robsonsobral, that already works. If you need a "slug" frontmatter property for Hugo, just add it to your config and the CMS won't pick it up or use it at all. Slugs, for our purposes, are related to the source file name only, and your SSG is concerned with the slug for the built file name.

erquhart avatar Apr 03 '18 20:04 erquhart

Thank you for your attention, @erquhart . Please, forgive my late answer.

What will happen if there's a slug field on front matter, after you allow the edition of slug? How to deal with two fields with the same name?

robsonsobral avatar Apr 04 '18 13:04 robsonsobral

The editable slug will be an internal value, we won't output it to frontmatter. It should be made available for reuse at some point, though, as requested in #450 and #1063.

erquhart avatar Apr 04 '18 14:04 erquhart

As I see it, you can use it in frontmatter if you want, but you could also set your own string. I'm not saying this is exactly how it will work, but a possible config format would be:

# No output to frontmatter.
- {label: "Slug", widget: "slug"}

# Save generated slug in frontmatter.
- {label: "Slug", widget: "slug", name: "slug"}

# Manual slug in frontmatter.
- {label: "Slug", widget: "string", name: "slug"}

tech4him1 avatar Apr 04 '18 16:04 tech4him1

Can we customize the field label? If we have a slug field on frontmatter, we could and with confusing labels.

robsonsobral avatar Apr 12 '18 11:04 robsonsobral

I'm not expecting there to be a typical field configuration for the slug editor, but I may be underthinking things. Either way, I'd expect output of the slug and other internal/meta values to be made available to fields as placeholder values via https://github.com/netlify/netlify-cms/issues/450. So you could do:

- {name: slug, widget: hidden, default: "{{slug}}"}

erquhart avatar Apr 12 '18 21:04 erquhart

@tech4him1 I wouldn't expect a slug widget - here's your example as I'd expect it:

# No output to frontmatter.
# (automatic)

# Save generated slug in frontmatter.
- {label: "Slug", name: "slug", default: "{{slug}}"}

# Manual slug in frontmatter.
# (same as above since the slug is editable as a meta field)

I can't think of a use case for allowing the slug to be configured as a field. You?

erquhart avatar Apr 18 '18 20:04 erquhart

This should be tagged high priority or something.

I’ve just had to redo my entire frontmatter because of this single issue. Posts couldn’t be created and it wasn’t even obvious why.

I don’t think there needs to be a super special UI for the slug — perhaps the default behavior, as it is now for title, would be showing the slug in small lettering underneath that with an edit button? I think something like that is done with WordPress. Otherwise just expose it like any other field.

mistermantas avatar Aug 08 '18 11:08 mistermantas

Starting in v2.0 there is an error message for this -- at least an improvement on the silent breakage we had before.

tech4him1 avatar Aug 11 '18 21:08 tech4him1

That’s… not really the case in my experience. If there was no field like title (even if there was Title) it would try to publish and throw an error in console and that’s it.

Maybe that should be taken into account too.

mistermantas avatar Aug 12 '18 10:08 mistermantas

@mistermantas That should already be throwing an error. Would you mind opening a new issue with an example config so that I can get that to at least throw an error?

tech4him1 avatar Aug 12 '18 15:08 tech4him1

It would also be nice if there was another placeholder like {{random}} generating a random string to use in the slug. I'd like to create a collections with items having no unique field, and just using a slug like slug: "{{year}}-{{month}}-{{day}}-{{hour}}_{{minute}}_{{second}}" feels a bit risky.

Another possibility to minimize the chance of colliding would be to add milliseconds as another field.

therealshark avatar Aug 22 '18 10:08 therealshark

Are we abstracting too much by calling it a slug instead of a filename or id? I think it is important the file name be thought of more as the resource identifier (id) for the item, not necessarily the slug value that will be used on a possible website. The primary focus of a CMS is about editing and managing content more than it is about creating a specific website.

In the old days of using prose to make editing GitHub hosted markdown files with front matter easier, the file name was simply a field you filled out when making a new item. Link to prose config docs. img

The simplest, most straightforward option for defining a file name is to have a form input field presented to the user. I'd like to see this be the default behavior when no slug property is provided to the collections config.

The next step in convenience is automatically creating the initial file name based on a template. I suggest passing the values of the fields to the template. So instead of {{ slug }} it would be {{ title }}. If you want a date add a date field. If you want the date in a special format use the the field date option for format in the config. Perhaps we add something like a slugFormat to the date field config options if the slug needs a different format from how it gets saved to the file. But that gets messy.

before

create: true
slug: "{{year}}-{{month}}-{{day}}-{{slug}}"
fields:
  - {label: "Title", name: "title", widget: "string"}

after

create: true
slug: "{{date}}-{{title}}"
fields:
  - {label: "Title", name: "title", widget: "string"}
  - {label: "Created Date", name: "date", widget: "datetime", format: "YYYY-MM-DD"}

Fields used in the template should be required to prevent unexpected results. If you want a "random" element for the slug add a hidden field with a widget that creates a random string portion. Access its value in the template like any other field.

webmasterkai avatar Jan 04 '19 19:01 webmasterkai

Are we abstracting too much by calling it a slug instead of a filename or id? I think it is important the file name be thought of more as the resource identifier (id) for the item, not necessarily the slug value that will be used on a possible website.

This 👆👆

@webmasterkai I was glad to see your comment as I recently came to the same conclusion: we should not be calling the filename "slug". It's confusing, and I'm betting most folks thumbing this issue up are thinking about the path to their live content and not the filename in the repo. @robsonsobral made a similar point a while back as well.

The more I consider this, the more I realize this whole slug editing thing is almost a non-issue. Static site generators don't care about filenames much (except for extensions), and they typically derive the actual slug and URL from your frontmatter and configuration.

Example I just gave in https://github.com/netlify/netlify-cms/issues/1576#issuecomment-459885997:

For example, let's say you have a Hugo site, a blog post frontmatter might look like this:

---
title: A Clear Performance Comparison between Gatsby and WordPress
slug: gatsby-vs-wordpress-performance
---

And your Hugo config would include something like:

permalinks:
  posts: /:year/:month/:slug/

Anyone think I'm oversimplifying here? Example use cases that require more?

@barthc I'd especially like to hear from you considering your recent work in this area.

erquhart avatar Feb 02 '19 02:02 erquhart

@erquhart I think static site generators do care a lot about filenames (and folder names).

The first choice is to use the filename for the output path. See for example in Hugo's Content Organization:

Hugo assumes that the same structure that works to organize your source content is used to organize the rendered site.

The slug in frontmatter is only meant to override this behavior if needed. Otherwise it's much better to use the folder and filename as a path. Unrelated file names would make it unnecessary hard to find the content file for a specific blog post, for example.

Not only file names matter but also folder names. As is discussed in #1472 with so called Page Bundles, assets and markdown files are stored in a folder. The entire folder will be copied and rendered to the final site.

content/
├── posts
│   ├── my-post-1
│   │   ├── index.md
│   │   └── post-image.jpg
│   ├── my-post-2
│   │   ├── index.md
│   │   └── post-image.jpg
│   ├── _index.md
│   └── overview-image.jpg

will render to:

public/
├── posts
│   ├── my-post-1
│   │   ├── index.html
│   │   └── post-image.jpg
│   ├── my-post-2
│   │   ├── index.html
│   │   └── post-image.jpg
│   ├── index.html
│   └── overview-image.jpg

For sluggification to work with this, Netlify CMS would need to allow changing the folder name.

marcojakob avatar Feb 02 '19 10:02 marcojakob

That's a very good point, thanks for laying it out. I guess I mostly see sites using custom permalinks via frontmatter rather than going with filename driven defaults, so it didn't seem that relevant, but it's a smart default, agreed.

erquhart avatar Feb 02 '19 15:02 erquhart

Update:

Some SSG's, like Jekyll, respect a slug value in the frontmatter. Until recently the {{slug}} built in placeholder made it impossible to reference a field named slug in your slug configuration, but as of Netlify CMS 2.4.2 you can now do {{fields.slug}} to reference a field by that name. That means you can control your filename in a predictable way:

collections:
  - name: posts
    folder: posts
    slug: {{fields.slug}}
    fields:
      - {name: title, label: Title}
      - {name: slug, label: Slug}
      - {name: body, label: Body, widget: markdown}

Note: this does not provide any way to change the filename after the initial save.

It's manual, but it's technically a minimum viable approach that satisfies the goals of this issue:

  • Visible: just a field
  • Customizable: can be a combination of fields via slug config
  • Fail-safe: the field value is sanitized and sluggified, and same-named files are rejected at the API level

We should keep the issue open for a more automated approach that's workable for non-technical editors, but this is at least a good stop gap for folks that need it.

erquhart avatar Feb 13 '19 15:02 erquhart

@erquhart that approach do not allow the field to be sluggified automatically from the title, it will need to be manually set each time. Can you please confirm. Ideally the field should be visible and updated once the title is set, but in this case if is empty it will fail, and will force manual entry. In a different note, without talking about slug update, it will be nice to be able to create a field with the contents of the slug field just like path : "/example/{{slug}}"

erodrig avatar Mar 10 '19 08:03 erodrig