Can i avoid LayoutId change when published layout?

hello, i need your help

when i checkout layout using API and publish when edited i see LayoutId change every time when published, so i need to avoid Layout change when published layout using API. can i do that?.

thank you for your help.

1 Like

The ID changing after an edit of a layout is an expected behavior since Xibo’s most recent upgrades> However what you can do is make a call to the API to find the ID of the layout base upon its name/tag or similar.

1 Like

This is an extremely confusing change that has essentially broken my custom CMS as I relied on those layout ids and region ids to alter the default layouts for all my displays.

I have done what you said here and send a query to find the layoutid by using the layout’s name. This works fine after I checkout and then publish a layout. The problem I am seeing though is that when a layout gets checked out the layout is set to “draft” and although it maintains the last published id in the layout list it seems to get the new layoutid and region ids that it will have once published when in the designer mode. Doing a query based on the name while the layout is in draft seems to return the current layout and region ids and therefore I can’t do any modifications to the regions because they are essentially new regions within a new layout but I can only access the old ids.

Am I missing something here? I assume it is impossible to make a change to a region or playlist when the layout has a published status?

@Ericccd I see you’ve created a separate topic with the same question, I will answer here and cover both topics as the answer is the same.

Why we’ve made this change
In previous CMS versions, once you created a layout it had assigned layoutId and that didn’t change over time.
What that means is, every time you open layout designer and make any changes to the layout, players that have this layout in active schedule will be notified. Therefore they will download the changes once they are made - which for some use cases might be suitable, for a lot of other cases that is not the desired workflow.
It was also possible to add say empty region or an invalid widget or something not set entirely correct yet, but since that’s all on one and the same layout, it would be pushed to the players.

In version 2, we have introduced Draft and Publish layout status.
Once layout is published and scheduled, that version of the layout will be sent to the players.
Now, you can enter designer, checkout the layout, which will change its status to Draft.
You can edit the draft, make any changes you wish and that in itself will not affect the players.
Players will get notified about changes only after you publish the layout, at which point they will download this new layout and display it.

In version 2.1 you will also be able to set a specific date at which the layout should be published ie
you can make changes ahead of time, then let’s say you don’t want the players to update the layout just yet, but instead you know that new content should be displayed from a specific date and you don’t necessarily want to publish it manually either, so you can do just that - set a date to publish and let the XTR take care of that for you.

Answer to your question.

Now that we have some background behind the change described, let me answer your question.

As per your other topic, you’ve discovered that the checkout call, returns the new layoutId, which you can use.
However, you can also access the draft layouts via API without calling checkout ie let’s assume you have some drafts you want to edit via API already ie you don’t want to call checkout you just want to get their layoutIds.

there are 2 parameters to consider here which you can use with the get layouts API call.

parentId - when the draft gets created it will have the original (parent) layoutId in the parentId paramter.
That will be easiest to get the layoutId you need.

publishedStatusId - 1 published, 2 draft, technically you can get an array of drafts and then exclude those with parentId null to get the actual drafts


Let’s say I want to edit a draft and that I happen to know the id of it, I can call

Alternatively, if you do not know the original layoutId, you can search by name, tag etc.

which will give me ( I won’t paste the full response here)

"layoutId": 450,
        "ownerId": 1,
        "campaignId": 394,
        "parentId": null,
        "publishedStatusId": 2,
        "publishedStatus": "Draft",

I can see it’s a draft and that it has parentId null ie 450 is the layoutId you can see in web ui ie the original layout id.

Now, I can call:

which will give me ( I won’t paste the full response here)

        "layoutId": 452,
        "ownerId": 1,
        "campaignId": 394,
        "parentId": 450,
        "publishedStatusId": 2,
        "publishedStatus": "Draft",

It is a draft and the parentId is 450, as such I know the 452 is the new layoutId of the draft, once you publish the layout you will see the 452 in web ui as well.

Of course the API call should be with embed=regions,playlists,widgets parameter, if you need the regionIds etc, but you already knew that.

I hope that helps.


ho!..thank you so mush!. i understood for get daft LayoutId using /api/layout?parentId=xxx but i can not find this solution in your API document. please, update API document

I have a Xibo installed off docker image xibo-docker-2.0.1 which is not honoring the ‘parentId’ query parameter.
Looking into the code, in .../www/cms/lib/Controller/Layout.php, I see that the ‘parentId’ is missing in the following code segment:

    // Get all layouts
    $layouts = $this->layoutFactory->query($this->gridRenderSort(), $this->gridRenderFilter([
        'layout' => $this->getSanitizer()->getString('layout'),
        'userId' => $this->getSanitizer()->getInt('userId'),
        'retired' => $this->getSanitizer()->getInt('retired'),
        'tags' => $this->getSanitizer()->getString('tags'),
        'exactTags' => $this->getSanitizer()->getCheckbox('exactTags'),
        'filterLayoutStatusId' => $this->getSanitizer()->getInt('layoutStatusId'),
        'layoutId' => $this->getSanitizer()->getInt('layoutId'),
        'ownerUserGroupId' => $this->getSanitizer()->getInt('ownerUserGroupId'),
        'mediaLike' => $this->getSanitizer()->getString('mediaLike'),
        'publishedStateId' => $this->getSanitizer()->getInt('publishedStateId'),

Am I using the wrong docker image? Is there a later one that I can use? Thanks

1 Like

That does seem like an oversight, indeed the parentId seems to be missing from 2.0.3 as well, I’m sorry for that.

The example I’ve made in this topic was using 2.1 CMS where this was possible, however it would seem we didn’t add it to 2.0.x release.

I’ve created an issue about it here -

Apologies for the inconvenience

waiting for xibo 2.1.x stable

Arrrgh. I sort of understand the design choice made here with the new 2.x layout workflow, but this seems really convoluted. Why not simply introduce “versions” to layouts, Instead of destroying the nice, reliable, unchanging, canonical layout ID?

It seems that, by altering the layout ID every time a layout is changed, it needlessly removes any permanent reference to the layout. Relying on layout name, tag, or some other arbitrary attribute means that we can no longer make absolute reference to a layout in the system.

For example, if I were to rely on looking up layouts by their name… if my target layout is called ‘test’ and I have layouts named ‘test’, ‘testagain’, and ‘testanother’ - if I do an api/layout query to look for ‘test’, will I not get all three back? Meanwhile won’t the region, playlist, and widget IDs also become worthless, because these will be constantly changed as well?

Sorry - I know you guys have worked long & hard on 2.x and here I am showing up at the last minute to complain; I probably should have paid more attention to the 2.x development branch a long time ago. This is just a particularly jarring change to try and assimilate.

I agree with the above. I came here to post a bug report, but found this thread and now realise it’s a feature :frowning:
I too understand some of the reasoning behind it, but it seems like an odd way to implement versioning.

This mechanism has been designed in since the start and is why each Layout has its own specific Campaign and campaignId. It is also why we do scheduling on campaignId.

When you return a Layout in the API you get its campaignId which you can use for this purpose.

There are a few places where we dropped the ball a bit (such as proof of play returning the layoutId, change layout actions, etc), and we are working to fix those for 2.1.

There are multiple implications to this as the layoutId is used to store the XLF file, in the schedule XML, in the required files XML. Having a new version would mean an entire new schema for XMDS and breaking changes for all Players.

We have thought this through :blush: and the mechanism we’ve gone with does provide the functionality with the smallest impact possible. However if your use case cannot be solved with campaignId let us know what you’re trying to do and we can see about adding some helpers to the API to achieve it.

Dan - thank you. Both for the quick reply, and all the work you guys do – Xibo is consistently one of the most reliable, and admin-friendly open source projects I use.

I did not realize that campaignId was an immutable value that can be referenced – I had assumed that it was only relevant in the context of Campaigns, which we rarely use (at least consciously) in our Xibo environment.

Now that I’ve played with campaignId a bit, I can see that it should work perfectly for our purposes!

1 Like

Fantastic - really pleased to hear campaignId will solve your concerns - and thanks for your other kind comments :blush:

Hi Dan,

CampaignId sounds promising. Would you mind offering a couple of examples as to how this might work in substitution for layoutId in the API?

I’ve just set up a fresh docker install of 2.0.3 and am testing the /layout api with Postman.

I have added three layouts and can filter by LayoutId (e.g. /layout?layoutId=1). As expected, only the layout with ID 1 is returned.

But if I try to filter on campaignId it doesn’t work - all layouts are returned (e.g. GET /layout?campaignId=1).