[IMPLEMENTED] Prevent referenced content from being deleted

I would like to prevent a content from beeing deleted if it is referenced by other contents.

For example, if I have an “article” that references an “author”, this author cannot be deleted.

I checked the ContentDto in the delete method of the ContentsPageComponent, but it seems there is no field that can help me to know if the content beeing deleted is referenced.

Is there a current way to do this without many customizations in the Squidex core?

No, right now there is nothing. But there are plans to add a sidebar to a content item to show all contents that reference this item and then we can also implement this feature.

1 Like

Hello, Sebastian.

I saw that there is a GetReferencedIds method, but it does not return the ids that are beeing referenced inside an array field that contains a reference field.

I tried to implement a new method including those ids, but I faced some problems using this method in a query.

So, is there a current way to do this? That is, return all ids referenced by a content, including those ones inside array fields?

Thank you.

Hi, are you sure. That sounds like a bug then.

1 Like

There are tests for that: https://github.com/Squidex/squidex/blob/master/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ExtractReferenceIds/ReferenceExtractionTests.cs

Thank you, Sebastian. I’ll check it now.

I found that the problem was in the method Visit(IArrayField field). The TryGetValue(nestedField.Name, out var nestedValue) was always returning false, because the keys in the object was the fields’ ids, not the names.

If we change it to (or add) item.TryGetValue(nestedField.Id.ToString(), out var nestedValue), it works and the method then returns all referenced ids, including those ones inside array fields.

Depends where we use the code.

When the content is stored int the DB all names are converted to Ids, but otherwise we always use the names.

Hi, I have forgotten this task, but have you made any progress?

Hi, Sebastian. Yes, we have accomplished what we needed. We created some methods called “Find Referers”. Here are the methods, in the sequence they are called:

ContentsController’s DeleteContent:

var referrers = await contentQuery.FindReferrersAsync(Context, name, id);

if (referrers.Count() > 0)
{
    return StatusCode(403, new { Message = Resources.Messages.ContentReferencedCannotBeDeleted });
}

ContentQueryService:

public async Task<IEnumerable<Guid>> FindReferrersAsync(Context context, string schemaIdOrName, Guid id, long version = -2)
{
    Guard.NotNull(context);

    var schema = await GetSchemaOrThrowAsync(context, schemaIdOrName);

    CheckPermission(context, schema);

    return await contentRepository.FindReferrersAsync(context.App, schema, id);
}

MongoContentRepository

public Task<IEnumerable<Guid>> FindReferrersAsync(IAppEntity app, ISchemaEntity schema, Guid id)
{
    Guard.NotNull(app);
    Guard.NotNull(schema);

    using (Profiler.TraceMethod<MongoContentRepository>())
    {
        return contents.FindReferrersAsync(schema, id);
    }
}

MongoContentCollection

public async Task<IEnumerable<Guid>> FindReferrersAsync(ISchemaEntity schema, Guid id)
{
    var fields = Builders<MongoContentEntity>.Projection.Include(x => x.Id);
    var find = Collection.Find(x => x.ReferencedIds != null && x.ReferencedIds.Contains(id) && !x.IsDeleted).Project(x => x.Id).ToList();
    return await Task.FromResult(find);
}

PS: As you can see, In the controller we are using resources to translate the messages.
PS: As we return a 403 status code, the client app already handles it and shows the error.

Awesome.

If you want to contribute this back to Squidex it would be welcome.

I would request a few changes:

  1. It would need to be configurable to prevent breaking changes.
  2. The UI should have a way to enforce it. (Delete anyway)
  3. The validation should be part of the GuardContent class
  4. I would only request one item. No need to get all referenced content items (same story as Any() vs Count() > 0 in LINQ)

Hi, Sebastian. It would be great to contribute to Squidex.

At this moment I’m no more working on the project in which we use Squidex, but I can try to do it.

I’ll fork the repository and start working, and as soon as I have something done, I show you. Ok?

1 Like

Sounds good :)… thank you very much

I have implemented this. I have also added the feature for assets and added a confirm dialog where you can force a delete even if it is referenced. To make the confirm dialogs less annoying I have also implemented a “Remember my Decision” checkbox for the dialog.