Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make sluggification visible, customizable, and fail-safe #445

Open
erquhart opened this issue Jun 6, 2017 · 55 comments
Open

Make sluggification visible, customizable, and fail-safe #445

erquhart opened this issue Jun 6, 2017 · 55 comments

Comments

@erquhart
Copy link
Contributor

erquhart commented Jun 6, 2017

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)
@Benaiah
Copy link
Contributor

Benaiah commented Jun 6, 2017

@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).

@erquhart
Copy link
Contributor Author

erquhart commented Jun 6, 2017

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 erquhart added this to the 1.0 milestone Jun 28, 2017
@erquhart
Copy link
Contributor Author

May happen in tandem with #180.

@tech4him1
Copy link
Contributor

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
Copy link
Contributor

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

@aperep
Copy link

aperep commented Sep 20, 2017

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.

@t1merickson
Copy link

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

@erquhart
Copy link
Contributor Author

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.

@bjrn
Copy link
Contributor

bjrn commented Feb 16, 2018

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

@erquhart
Copy link
Contributor Author

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

@hanneskuettner
Copy link
Contributor

I stumbled across this issue myself.
Now that the config, entry data and collection is available to all control widgets (#3672) I was able to implement a custom slug control widget that allows the following:

... # whatever collection
- slug: '{{field.slug}}'
- fields:
  - name: title
     label: Title
     widget: string
  - name: slug
     label: Slug
     widget: slug
     slug: 'custom-slug-{{title}}'
  • Have a slug value field with a custom slug widget (that's basically a string widget) that can be used for the entry slug with fields.slug
  • The value in slug is automatically updated and correctly slugified according to the slug pattern and the root slug configuration
  • Once an entry is saved the slug is no longer updated when the dependent fields are updated
  • The user can manually update the slug but receives a warning that this might break existing URLs since it is a slug field

This works very well for me (I think at least). It still does not provide a way for a user to forcefully update the filename through the slug though.

Once it is a bit more polished I will open a PR (in the next few days) and then I would love to continue this discussion.

@keegan-lillo
Copy link
Contributor

@hanneskuettner I look forward to your PR. In the mean time, I came up with a fairly decent solution that might help others.

I used Nested Collections to basically give me the same effect since the slug (file name) is set via the meta.path field.

config.yaml

collections:
  - name: 'pages'
    create: true
    label: 'Page'
    folder: 'src/content/pages'
    nested: { depth: 100, summary: '{{meta.path}}' }
    meta: { path: { widget: string, label: 'Path', index_file: 'index' } }
    fields:
      - label: 'Title'
        name: 'title'
        widget: 'string'

      - label: 'Body'
        name: 'body'
        widget: 'markdown'

And then if I add two items in the collection with paths set to: dir-a/dir-b/item-a and dir-a/dir-c/item-b
This results in a src/content/pages directory looking like:

src/content/pages:
  dir-a:
    dir-b:
      item-a:
        - index.md
    dir-c:
      item-b:
        - index.md

image

I'm using Gatsby so it's reasonably straightforward to generate slugs at build time using the technique from this guide.

@mikestopcontinues
Copy link

Here's the thing: Without keeping the filename in sync with the slug, it's impossible to look up a single post without parsing the frontmatter of all files in the same collection. E.g., if my app wants the post with slug what-a-nice-post, the app needs to load all of the posts into memory. This kinda sucks when used alongside NextJS, which supports incremental static generation as well as incremental cache invalidation.

For this reason—and since URL slugs must be unique anyway—I think it makes more sense to update the filename whenever the slug changes.

Regarding how and when the slug changes, I like @hanneskuettner approach. The slug should stop auto-updating once the document is published (which happens at varying times depending on if you use the editorial workflow). After that, a notice is fair, as well as preventing a user from overwriting another file or making a change that would cause merge errors (if there are multiple editorial edits open on the file that would be renamed).

Honestly, I think Wordpress really nails their slug behavior, especially in cases where extra bits are added to the complete path. #377 has a screenshot that at least partly shows this behavior.

@hanneskuettner If you would, I'd very much appreciate seeing your work on that slug field. :)

@michalhudecek
Copy link

What's the progress on this?

@erezrokah
Copy link
Contributor

Hi @michalhudecek this issue is not an easy lift. Do you have a specific use case you're trying to solve?

@michalhudecek
Copy link

I would like to make the slug editable with a warning for editors that this might break already shared links.

@erezrokah
Copy link
Contributor

I would like to make the slug editable with a warning for editors that this might break already shared links.

Will the approach in #445 (comment) satisfy your use case?

@michalhudecek
Copy link

Yes, that seems like a perfect solution.

@andreasbalevik
Copy link

andreasbalevik commented Mar 7, 2021

Just want to share. Problem in my case is that slug does not sync with the title.

I use jekyll with netlify cms. When creating a page in Netlify "Hello World Page" this will be saved as "hello-world-page-md". In jekyll this will be generated to "/hello-world-page.html" when the site is built. Problem is when changing the title of the page to "Hello Again World", the markdown is still "hello-world-page.md and therfore the generated page will be "/hello-world-page.html".

This causing problems with url not reflecting the page title. I think it is problematic to share a url not reflecting the content of the page.

Jekyll can overwrite the generated url with slug variable provided in font matter. Problem is i cant use jekylls liquid variables in font matter. So it wont be dynamic.

I could create a field "slug" in the netlify collection. But the only way that would work for me was if it mirrored title, and the field was hidden. So the solution described here might work for me aswell.

@tylerbrostrom
Copy link
Contributor

Problem in my case is that slug does not sync with the title.

Updating slugs has to be an explicit action. Otherwise, content authors would unknowingly break backlinks all the time. You’d have to account for the update by generating redirect rules for the web server. Traditional CMSs—which may have access to the web server and thus could generate redirects—don’t even do this.

If you have access to the code (i.e. you’re not a non-technical content author), the most straightforward way is to just rename the file.

@mikestopcontinues
Copy link

@tylerbrostrom I think it should be explicit as well, but I'd much rather see this change happen in pieces than it be delayed another 4 years. The vast majority of users only have one content editor.

@andreasbalevik
Copy link

andreasbalevik commented Mar 8, 2021

@tylerbrostrom i agree.

But this may result in a dillemma.

Not having relevant keywords in domain / url could hurt your relevant search scoore, preventing you from getting the 1st spot in search lists.

But as you mentions, broken backlinks may be a bigger problem. Backlinks also impact the search scoore.

I cant prove that this is the case. If anyone can confirm that the url does not affect the search result, then I have no idea what is best.

Something crossed my mind. With netlify you can create a _redirects file in root. May be complicated. But if Netlify CMS could manage a file like that. Then changing the slug wouldnt be a problem with already shared links. Just a crazy thought.

@mikestopcontinues
Copy link

@andreasbalevik I'd prefer a formerSlugs array on the documents themselves for the user to handle based on platform. Some will need redirects, some won't because they're the location of new documents.

@erezrokah
Copy link
Contributor

Something crossed my mind. With netlify you can create a _redirects file in root. May be complicated. But if Netlify CMS could manage a file like that. Then changing the slug wouldnt be a problem with already shared links. Just a crazy thought.

Not crazy at all, but I don't think this should be done by a content management system as it's very specific to how the site is built and deployed. Perhaps a Netlify Build Plugin.

@karllhughes
Copy link

@hanneskuettner's solution looks great, but I'm guessing it's not implemented yet? When I tried it, I got this error in my editor:

No control for widget 'slug'.

Is this widget something I need to register separately?

@jmayergit
Copy link

jmayergit commented Apr 22, 2021

To further @hanneskuettner's example above,

widget

// index.html

<script>
  var SlugControl = createClass({
    getInitialState: function () {
      return { touched: false }
    },

    handleChange: function (e) {
      this.props.onChange(e.target.value)
      !this.state.touched && this.state.touched = true
    },

    render: function () {
      return h('input', {
        id: this.props.forID,
        className: this.props.classNameWrapper,
        type: 'text',
        value: this.state.touched ? this.props.value : this.props.entry.get('slug'),
        onChange: this.handleChange,
      })
    },
  })

  var SlugPreview = createClass({
    render: function () {
      return null // or show a preview
    },
  })

  var schema = {
    properties: {},
  }

  CMS.registerWidget('slug', SlugControl, SlugPreview, schema)
</script>

config

// config.yml

    slug: '{{fields.slug}}'
    fields:
      - { label: 'slug', name: 'slug', widget: slug, hint: 'slug' }

comparison with hanneskuettner's implementation

  • Have a slug value field with a custom slug widget (that's basically a string widget) that can be used for the entry slug with fields.slug 🙆
  • The value in slug is automatically updated and correctly slugified according to the slug pattern and the root slug configuration 🙅
  • Once an entry is saved the slug is no longer updated when the dependent fields are updated 🙅
  • The user can manually update the slug but receives a warning that this might break existing URLs since it is a slug field 🙅

➕ works with previously created entries

Notes

  • With the above implementation slugs are not automatically generated for new entries.
  • In hanneskuettner's (slug: 'custom-slug-{{title}}') example I'm not sure if field template tags can be referenced in fields themselves.
  • The slug can be configured using the slug template or a schema property
// config.yml

...
    slug: 'my-slug-template-{{fields.slug}}'
    fields:
      - { label: 'slug', name: 'slug', widget: slug, hint: 'slug', mySchemaProperty: 'my-schema-property' }
      
// index.html

...

      handleChange: function (e) {
        // use the schema property in your handler function
        // this.props.field.get('mySchemaProperty')
      }, 
    
    ...
    
    var schema = {
      properties: {
        mySchemaProperty: { type: 'string' },
      },
    }

@timtorres
Copy link

Something crossed my mind. With netlify you can create a _redirects file in root. May be complicated. But if Netlify CMS could manage a file like that. Then changing the slug wouldnt be a problem with already shared links. Just a crazy thought.

Not crazy at all, but I don't think this should be done by a content management system as it's very specific to how the site is built and deployed. Perhaps a Netlify Build Plugin.

Shopify does this behind the scenes pretty nicely. It edits a redirects file, adding a new entry every time a slug is changed. There's at least one 3rd party app that extends this by adding a redirects management UI. Scans for broken links or something like that and lets you manage multiple aliases for a single url.

But I do agree with you that the CMS itself should remain divorced from that in order to support different setups.

@erezrokah
Copy link
Contributor

Shopify does this behind the scenes pretty nicely. It edits a redirects file, adding a new entry every time a slug is changed. There's at least one 3rd party app that extends this by adding a redirects management UI. Scans for broken links or something like that and lets you manage multiple aliases for a single url.

I think https://github.com/sw-yx/netlify-plugin-no-more-404 is supposed to do some of that, but seeking maintainers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests