[Feature request]Automatic update of cross-linking between pages/chapters/books #1598

Closed
opened 2026-02-05 01:22:13 +03:00 by OVERLORD · 8 comments
Owner

Originally created by @davidwaroquiers on GitHub (Mar 17, 2020).

It would be good that links from one page/chapter/book to another page/chapter/book are automatically updated when this other page/chapter/book is moved or modified.

This would allow BookStack users to make a link to a given page/chapter/book and if this page/chapter/book is modified or moved, the link continues to work while currently the link gets broken. Of course there are special cases that would never work (e.g. if you delete a page/chapter/book).

One small example to make things clear. Let's assume I have one book called "Ongoing projects" and another one called "Archived projects". I have a chapter "ProjectABC" in "Ongoing projects". I also have a book called "Miscellaneous" that has a page with a link to "ProjectABC" in "Ongoing projects". If I move "ProjectABC" from "Ongoing projects" to "Archived projects", the link to "ProjectABC" in "Miscellaneous" is broken.

Any thoughts on this ? I don't know if this is easily doable, or even if this is somewhat already possible (I did not find), and if this would be of any use for a large audience. I am a BookStack beginner and at this stage I am sure I cannot contribute to such a feature. Nonetheless, it might be that this could be the case in the longer-term but I have no clear view on that.

Thank you for your hard work.

Originally created by @davidwaroquiers on GitHub (Mar 17, 2020). It would be good that links from one page/chapter/book to another page/chapter/book are automatically updated when this other page/chapter/book is moved or modified. This would allow BookStack users to make a link to a given page/chapter/book and if this page/chapter/book is modified or moved, the link continues to work while currently the link gets broken. Of course there are special cases that would never work (e.g. if you delete a page/chapter/book). One small example to make things clear. Let's assume I have one book called "Ongoing projects" and another one called "Archived projects". I have a chapter "ProjectABC" in "Ongoing projects". I also have a book called "Miscellaneous" that has a page with a link to "ProjectABC" in "Ongoing projects". If I move "ProjectABC" from "Ongoing projects" to "Archived projects", the link to "ProjectABC" in "Miscellaneous" is broken. Any thoughts on this ? I don't know if this is easily doable, or even if this is somewhat already possible (I did not find), and if this would be of any use for a large audience. I am a BookStack beginner and at this stage I am sure I cannot contribute to such a feature. Nonetheless, it might be that this could be the case in the longer-term but I have no clear view on that. Thank you for your hard work.
OVERLORD added the 🛠️ Enhancement🔨 Feature Request🏭 Back-End labels 2026-02-05 01:22:13 +03:00
Author
Owner

@homotechsual commented on GitHub (Mar 17, 2020):

I'd like this idea to become reality - I don't know how much work it would be - but building the links at render-time using the underlying database/content IDs (or using a custom link handler like GitHub/MediaWiki do) - I do however suspect that this would be a fairly significant chunk of work.

@homotechsual commented on GitHub (Mar 17, 2020): I'd like this idea to become reality - I don't know how much work it would be - but building the links at render-time using the underlying database/content IDs (or using a custom link handler like GitHub/MediaWiki do) - I do however suspect that this would be a fairly significant chunk of work.
Author
Owner

@ssddanbrown commented on GitHub (Mar 18, 2020):

Thanks for the suggestion @davidwaroquiers. This is something that pops up often for various reasons. Rather than link to past discussions I thought I'd collate my up-to-date thoughts on this here, sorry if it seems like a bit of a ramble.

Current State

Just some insight into the current system, There are a couple of mechanisms within BookStack that can help alleviate (But definitely not remove) this issue:

  1. When page url "slugs" change, or when moved to a different book, BookStack can still find the page via the old URL in many cases as it will look to the revision system so find by past book/page slug names.
    • This is only on pages though so doesn't help with books or your example case of chapter moving.
  2. For pages, there is a permalink-style system that gives you a link to a page via an ID instead of url-slug. You can get this by highlighting text within a block, which will show a little pop-over with a URL. This URL will link directly to the selected section but you can remove the hash and everything after to link just to the page instead of a section on that page.
    • Someone recently mentioned they might look to roll this system out to other entity types (Books, Shelves, Chapters).

My Thinking on Urls

I could have just used ID-based URLs in BookStack from the start, in fact that would make it much easier in terms of the code side of things, but I purposefully went out the way to have URLs with the content name in. I think there's a lot of value in providing some context in the URL. If that URL is shared then the receiver can instantly understand what that URL will likely lead to. Same when just coming across the URL in existing content.

Dynamic URLs have been suggested before, and there are even existing pull requests to incorporate such functionality, but it's really important to me that content is not stored in a way which needs to be dynamically parsed to be usable. User-created page content is the treasure of a BookStack instance. Control and portability of this content should remain with the user/admin as much as possible. Ideally I want it so that if someone's BookStack instance badly breaks, or the project becomes dead, the page content could be taken from the DB and re-used elsewhere without a great deal of hassle. It's the same reason we keep the generated HTML fairly flat with limited custom classes for styles. There is one feature that goes against this (Dynamic includes) but it's fairly hidden & suited for advanced users. Dynamic URL's also complicate the page editor systems quite significantly.

Moving Forward

I do acknowledge that the current potential for URL breaking can be an issue and ideally we want to ensure integrity when cross-linking and reduce any anxiety in performing such operations. I'll outlay a few options:

  1. Dynamically Generated URLs, rendered on view.
    • As above, not my preferred choice due to needing "proprietary" BookStack logic to render the content.
    • Complicates the page editor since those URL's will still need to be user-manageable and resolvable in the live-previews.
  2. Upon item slug change (move or rename) search existing content and update links from old to new.
    • Need to consider if that's a change we should create revisions for; If not, do we auto-update old revisions also?
    • Would need to do a full page-content search for such operations, which can't easily be indexed leading to performance drop-off with growth, especially in batch operations such as book-sorts.
  3. Create a new "Best of both" URL scheme that will search for items based on ID.
    • As a really rough example https://demo.bookstackapp.com/book/donkeys/page:553/barry-the-awesome-donkey.
    • Could be a good opportunity to re-think the best URL scheme for most users.
    • Would wonder if people are happy with having an id be that exposed. I know it's already accessible but not instantly visible as it stands, can kind of be a leak of info in some real cases.
    • Obviously would need to run alongside and be compatible with existing URL schemes to ensure backwards compatibility.

I'll mark this as open to discussion as I'd be interested in feedback and other potential ideas. Thank you @davidwaroquiers for making the goal of this feature request clear, instead of requesting a specific implementation right away, makes it easier to have an open discussion to find the best implementation.

@ssddanbrown commented on GitHub (Mar 18, 2020): Thanks for the suggestion @davidwaroquiers. This is something that pops up often for various reasons. Rather than link to past discussions I thought I'd collate my up-to-date thoughts on this here, sorry if it seems like a bit of a ramble. #### Current State Just some insight into the current system, There are a couple of mechanisms within BookStack that can help alleviate (But definitely not remove) this issue: 1. When page url "slugs" change, or when moved to a different book, BookStack can still find the page via the old URL in many cases as it will look to the revision system so find by past book/page slug names. * This is only on pages though so doesn't help with books or your example case of chapter moving. 2. For pages, there is a permalink-style system that gives you a link to a page via an ID instead of url-slug. You can get this by highlighting text within a block, which will show a little pop-over with a URL. This URL will link directly to the selected section but you can remove the hash and everything after to link just to the page instead of a section on that page. * Someone recently mentioned they might look to roll this system out to other entity types (Books, Shelves, Chapters). #### My Thinking on Urls I could have just used ID-based URLs in BookStack from the start, in fact that would make it much easier in terms of the code side of things, but I purposefully went out the way to have URLs with the content name in. I think there's a lot of value in providing some context in the URL. If that URL is shared then the receiver can instantly understand what that URL will likely lead to. Same when just coming across the URL in existing content. Dynamic URLs have been suggested before, and there are even existing pull requests to incorporate such functionality, but it's really important to me that content is not stored in a way which needs to be dynamically parsed to be usable. User-created page content is the treasure of a BookStack instance. Control and portability of this content should remain with the user/admin as much as possible. Ideally I want it so that if someone's BookStack instance badly breaks, or the project becomes dead, the page content could be taken from the DB and re-used elsewhere without a great deal of hassle. It's the same reason we keep the generated HTML fairly flat with limited custom classes for styles. There is one feature that goes against this (Dynamic includes) but it's fairly hidden & suited for advanced users. Dynamic URL's also complicate the page editor systems quite significantly. #### Moving Forward I do acknowledge that the current potential for URL breaking can be an issue and ideally we want to ensure integrity when cross-linking and reduce any anxiety in performing such operations. I'll outlay a few options: 1. Dynamically Generated URLs, rendered on view. * As above, not my preferred choice due to needing "proprietary" BookStack logic to render the content. * Complicates the page editor since those URL's will still need to be user-manageable and resolvable in the live-previews. 2. Upon item slug change (move or rename) search existing content and update links from old to new. * Need to consider if that's a change we should create revisions for; If not, do we auto-update old revisions also? * Would need to do a full page-content search for such operations, which can't easily be indexed leading to performance drop-off with growth, especially in batch operations such as book-sorts. 3. Create a new "Best of both" URL scheme that will search for items based on ID. * As a really rough example `https://demo.bookstackapp.com/book/donkeys/page:553/barry-the-awesome-donkey`. * Could be a good opportunity to re-think the best URL scheme for most users. * Would wonder if people are happy with having an id be that exposed. I know it's already accessible but not instantly visible as it stands, can kind of be a leak of info in some real cases. * Obviously would need to run alongside and be compatible with existing URL schemes to ensure backwards compatibility. --- I'll mark this as open to discussion as I'd be interested in feedback and other potential ideas. Thank you @davidwaroquiers for making the goal of this feature request clear, instead of requesting a specific implementation right away, makes it easier to have an open discussion to find the best implementation.
Author
Owner

@davidwaroquiers commented on GitHub (Mar 18, 2020):

Thanks for your very complete answer @ssddanbrown and all your thoughts about this.

Just wondering if a solution could be something like a "back-reference list". Let me explain. When you add a link to another page/chapter/book/shelve (let's call this the TARGET) in a given page/chapter/book (let's call this the CURRENTLY_EDITED_ITEM), the TARGET would (somehow, I don't know how and where) get the information that it has a link to itself from CURRENTLY_EDITED_ITEM (again, somehow stored in this "back-reference list"). Now if TARGET is moved, instead of having to make the full-page content search it "knows" that CURRENTLY_EDITED_ITEM has a link to itself (because it is in the "back-reference list") and it could update that link automatically (actually update all the links in possibly other items, be it pages or chapters or else). Of course your comment about revisions still holds.

There are a few tricks also. If we then remove the link to TARGET in CURRENTLY_EDITED_ITEM, it should of course be removed from the "back-reference list". Same if TARGET is moved itself or modified, the "back-reference list" should be updated accordingly. Also probably it would somehow mean that an "internal link" is handled differently from an "external link" (If I'm not wrong, these are actually handled the same way right ?). Also wondering about backward incompatibilities in that case ... if indeed internal BookStack links are handled differently than a link to an external webpage, one option might be that "old" internal links stay as they are and it's up to the user to update those if this feature gets out at some point.

Somehow to me it kind of looks like your solution 2. but without the burden of the full-page content search that is needed when a page/chapter/book is moved or modified, because the pages, chapters, books that have a link to the edited item are known.

Also, that would mean that there could possibly be an additional back-reference feature in each page/chapter/book, something like "what are the pages/chapters/books that currently have a link to me ?" (e.g. in the Actions menu, something that could be called "Citations" or anything else).

I don't know if this is possible/easy to do, but it's just a thought.

Again, thank you for all this work and for the project. This is great.

Best,

David

@davidwaroquiers commented on GitHub (Mar 18, 2020): Thanks for your very complete answer @ssddanbrown and all your thoughts about this. Just wondering if a solution could be something like a "back-reference list". Let me explain. When you add a link to another page/chapter/book/shelve (let's call this the TARGET) in a given page/chapter/book (let's call this the CURRENTLY_EDITED_ITEM), the TARGET would (somehow, I don't know how and where) get the information that it has a link to itself from CURRENTLY_EDITED_ITEM (again, somehow stored in this "back-reference list"). Now if TARGET is moved, instead of having to make the full-page content search it "knows" that CURRENTLY_EDITED_ITEM has a link to itself (because it is in the "back-reference list") and it could update that link automatically (actually update all the links in possibly other items, be it pages or chapters or else). Of course your comment about revisions still holds. There are a few tricks also. If we then remove the link to TARGET in CURRENTLY_EDITED_ITEM, it should of course be removed from the "back-reference list". Same if TARGET is moved itself or modified, the "back-reference list" should be updated accordingly. Also probably it would somehow mean that an "internal link" is handled differently from an "external link" (If I'm not wrong, these are actually handled the same way right ?). Also wondering about backward incompatibilities in that case ... if indeed internal BookStack links are handled differently than a link to an external webpage, one option might be that "old" internal links stay as they are and it's up to the user to update those if this feature gets out at some point. Somehow to me it kind of looks like your solution 2. but without the burden of the full-page content search that is needed when a page/chapter/book is moved or modified, because the pages, chapters, books that have a link to the edited item are known. Also, that would mean that there could possibly be an additional back-reference feature in each page/chapter/book, something like "what are the pages/chapters/books that currently have a link to me ?" (e.g. in the Actions menu, something that could be called "Citations" or anything else). I don't know if this is possible/easy to do, but it's just a thought. Again, thank you for all this work and for the project. This is great. Best, David
Author
Owner

@ssddanbrown commented on GitHub (Mar 22, 2020):

@davidwaroquiers That's a really neat idea! Nice thinking!

Also probably it would somehow mean that an "internal link" is handled differently from an "external link" (If I'm not wrong, these are actually handled the same way right ?)

Yeah, All links currently handled the same as far as I remember.

Of course your comment about revisions still holds.

For that I'm thinking we should create a revision, so any "magic" BookStack is doing remains visible to the user and so they'll have the history in-case something goes work. It does mean that restoring an old revision may re-introduce broken links but hopefully that's a limited case, semi-expected & the new links will still be accessible via a revision if they want to merge content.

@ssddanbrown commented on GitHub (Mar 22, 2020): @davidwaroquiers That's a really neat idea! Nice thinking! > Also probably it would somehow mean that an "internal link" is handled differently from an "external link" (If I'm not wrong, these are actually handled the same way right ?) Yeah, All links currently handled the same as far as I remember. > Of course your comment about revisions still holds. For that I'm thinking we should create a revision, so any "magic" BookStack is doing remains visible to the user and so they'll have the history in-case something goes work. It does mean that restoring an old revision may re-introduce broken links but hopefully that's a limited case, semi-expected & the new links will still be accessible via a revision if they want to merge content.
Author
Owner

@setpill commented on GitHub (Apr 11, 2020):

Updating old references, to me, seems like an over-engineered solution. It would be better to switch to a url scheme that is both human-friendly (has the book/chapter/page title - at time of creation) as well as machine-friendly (has a (UU)ID reference so that the page can be found even if moved/renamed). That way, you solve the case where pages are linked from outside of bookstack (emails, for example) as well.

Auto-updating links might not even make sense! Take for example the following hierarchy:

  • Book "Home"
    • Chapter "Ongoing projects"
      • Page "Custom DnD table"
      • Page "Home gym"
    • Chapter "Past projects"
      • Page "Pick out nice plants"
  • Book "Car"
    • Chapter "Ongoing projects"
      • Page "Weird rattling noise"
    • Chapter "Past projects"
      • Page "Fix mirror"
  • Book "Overview"
    • Page "Ongoing projects"
      • Link to "Custom DnD table"
      • Link to "Home gym"
      • Link to "Weird rattling noise"
    • Page "Past projects"
      • Link to "Pick out nice plants"
      • Link to "Fix mirror"

What if I get everything I want for my home gym and move that project page from ongoing to past? I don't want that to auto-update the link in the "Overview" book. Maybe it would be possible to have a little indicator next to the link that the stub is outdated, and let whoever is taking care of that page decide what to do about it.

I'm not even sure of what the implications of auto-updating back-references would be if one changes the slug of a page that is referenced by pages one has no access to.

@setpill commented on GitHub (Apr 11, 2020): Updating old references, to me, seems like an over-engineered solution. It would be better to switch to a url scheme that is both human-friendly (has the book/chapter/page title - at time of creation) as well as machine-friendly (has a (UU)ID reference so that the page can be found even if moved/renamed). That way, you solve the case where pages are linked from outside of bookstack (emails, for example) as well. Auto-updating links might not even make sense! Take for example the following hierarchy: * Book "Home" * Chapter "Ongoing projects" * Page "Custom DnD table" * Page "Home gym" * Chapter "Past projects" * Page "Pick out nice plants" * Book "Car" * Chapter "Ongoing projects" * Page "Weird rattling noise" * Chapter "Past projects" * Page "Fix mirror" * Book "Overview" * Page "Ongoing projects" * Link to "Custom DnD table" * Link to "Home gym" * Link to "Weird rattling noise" * Page "Past projects" * Link to "Pick out nice plants" * Link to "Fix mirror" What if I get everything I want for my home gym and move that project page from ongoing to past? I don't want that to auto-update the link in the "Overview" book. Maybe it would be possible to have a little indicator next to the link that the stub is outdated, and let whoever is taking care of that page decide what to do about it. I'm not even sure of what the implications of auto-updating back-references would be if one changes the slug of a page that is referenced by pages one has no access to.
Author
Owner

@Wookbert commented on GitHub (Feb 6, 2021):

Just ran into a misbehaviour which fits into this issue: The four links shown this image link to different pages in a chapter in a book called Server – Installation & Wartung, which was renamed from Servers – Installation & Wartung (note the plural s). Strangely two links still worked, while the other two led to „Page not found“. If I manually fix those links they work again.

If I rename the book again e.g. to Serverz – ..., I get the same problem again: Two links get properly redirected (hovering over the links shows me that they are not changed, but getting redirected), while the other two are broken again.

image

As far as I can tell, the URLs have been copied & pasted from the browser’s URL field. If that’s not the proper way to do it, may I suggest and additional button for local (= BookStack-internal) links?

I would imagine the following procedure:

  1. Mark the word you want to link.
  2. Press the BookStack cross-reference button.
  3. An overlay window opens, from which shows a cascaded/collapsable view of all books, chapters and pages, giving you the option to quickly select the page you want to link to. Perhaps a fourth level with the cascaded view of the page’s headlines.

An on-the-fly search function in that link selection window to instantly find the page you are looking for would be the icing on the cake. And a readable/scrollable page preview, to verify one’s correctly linking to the desired content, the cherry on top.

@Wookbert commented on GitHub (Feb 6, 2021): Just ran into a misbehaviour which fits into this issue: The four links shown this image link to different pages in a chapter in a book called `Server – Installation & Wartung`, which was renamed from `Servers – Installation & Wartung` (note the plural s). Strangely two links still worked, while the other two led to „Page not found“. If I manually fix those links they work again. If I rename the book again e.g. to `Serverz – ...,` I get the same problem again: Two links get properly redirected (hovering over the links shows me that they are not changed, but getting redirected), while the other two are broken again. ![image](https://user-images.githubusercontent.com/3104762/107125223-e6265b80-68a8-11eb-991b-2c9877f49905.png) As far as I can tell, the URLs have been copied & pasted from the browser’s URL field. If that’s not the proper way to do it, may I suggest and additional **button for local (= BookStack-internal) links**? I would imagine the following procedure: 1. Mark the word you want to link. 2. Press the BookStack cross-reference button. 3. An overlay window opens, from which shows a cascaded/collapsable view of all books, chapters and pages, giving you the option to quickly select the page you want to link to. Perhaps a fourth level with the cascaded view of the page’s headlines. An on-the-fly search function in that _link selection window_ to instantly find the page you are looking for would be the icing on the cake. And a readable/scrollable page preview, to verify one’s correctly linking to the desired content, the cherry on top.
Author
Owner

@rickhall commented on GitHub (Mar 17, 2021):

@Wookbert Your last suggestion of being able to drill down from books into the sections is just intuitively how I assumed it would work and was surprised when it didn't allow me to do that, so I'm in agreement with you on that.

Regarding the URL scheme, it does sound like it makes sense to adopt a hybrid approach that exposes both the ID and content. Although, for me personally, I'd also be fine with just the ID and not content. I understand the desire to include the content, but I think it is more idealism than necessity. For example, no one laments that YouTube URLs only include the video ID. Regardless, only exposing the content in the URL on a system designed for creating editable content is probably not the optimal choice.

Great work, nonetheless. Thanks.

@rickhall commented on GitHub (Mar 17, 2021): @Wookbert Your last suggestion of being able to drill down from books into the sections is just intuitively how I assumed it would work and was surprised when it didn't allow me to do that, so I'm in agreement with you on that. Regarding the URL scheme, it does sound like it makes sense to adopt a hybrid approach that exposes both the ID and content. Although, for me personally, I'd also be fine with just the ID and not content. I understand the desire to include the content, but I think it is more idealism than necessity. For example, no one laments that YouTube URLs only include the video ID. Regardless, only exposing the content in the URL on a system designed for creating editable content is probably not the optimal choice. Great work, nonetheless. Thanks.
Author
Owner

@ssddanbrown commented on GitHub (Sep 5, 2022):

Thanks for the input all.
I did explore the idea of changing the URL scheme to be uid-based, within #3520, but I concluded thinking there's no ideal approach (That would satisfy all) and any change is going to have a lot of friction and ongoing consequences.

Instead I've looked to address this fairly directly along the lines of the suggestion by @davidwaroquiers above.
Storing/tracking backreferences has value in itself, as reflected in #2864.
Plus it allows us to address this issue now, without larger scale overhauls, in a fairly efficient manner.

The work for this has been primarily added as part of #3656, and will be part of the next feature release.
Note: This won't solve every possible scenario; For example, restoring old revisions may use old links, but this should take take of the vast majority of problematic cases. Further issues could be opened non-handled cases need to be addressed (If deemed worthwhile).

Note: You'll need to run a maintenance action or command to update this reference index for old pages, this will be detailed in release notes for the next version.

@ssddanbrown commented on GitHub (Sep 5, 2022): Thanks for the input all. I did explore the idea of changing the URL scheme to be uid-based, within #3520, but I concluded thinking there's no ideal approach (That would satisfy all) and any change is going to have a lot of friction and ongoing consequences. Instead I've looked to address this fairly directly along the lines of the suggestion by @davidwaroquiers above. Storing/tracking backreferences has value in itself, as reflected in #2864. Plus it allows us to address this issue now, without larger scale overhauls, in a fairly efficient manner. The work for this has been primarily added as part of #3656, and will be part of the next feature release. Note: This won't solve every possible scenario; For example, restoring old revisions may use old links, but this should take take of the vast majority of problematic cases. Further issues could be opened non-handled cases need to be addressed (If deemed worthwhile). Note: You'll need to run a maintenance action or command to update this reference index for old pages, this will be detailed in release notes for the next version.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/BookStack#1598