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

feat: Re-introduce deleting languages of a page #443

Open
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

fsbraun
Copy link
Member

@fsbraun fsbraun commented Jan 15, 2025

Description

If DJANGOCMS_ALLOW_DELETING_VERSIONS is set to True, the delete translation menu is offered.

fixes #442

Related resources

Checklist

  • I have opened this pull request against master
  • I have added or modified the tests when changing logic
  • I have followed the conventional commits guidelines to add meaningful information into the changelog
  • I have read the contribution guidelines and I have joined #workgroup-pr-review on
    Slack to find a “pr review buddy” who is going to review my pull request.

Summary by Sourcery

Re-introduce the ability to delete page translations if DJANGOCMS_ALLOW_DELETING_VERSIONS is set to True.

Bug Fixes:

Enhancements:

  • Obtain the correct page content for copying plugins across languages.

Summary by Sourcery

Restore the "Delete Translation" menu in the language menu if DJANGOCMS_ALLOW_DELETING_VERSIONS is set to True.

Bug Fixes:

  • Restore the missing delete translation menu when DJANGOCMS_ALLOW_DELETING_VERSIONS is enabled.

Enhancements:

  • Obtain the correct page content for copying plugins across languages.

Copy link

codecov bot commented Jan 15, 2025

Codecov Report

Attention: Patch coverage is 78.94737% with 4 lines in your changes missing coverage. Please review.

Project coverage is 90.96%. Comparing base (81306a0) to head (1c11746).

Files with missing lines Patch % Lines
djangocms_versioning/cms_toolbars.py 78.94% 2 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #443      +/-   ##
==========================================
- Coverage   91.09%   90.96%   -0.13%     
==========================================
  Files          72       72              
  Lines        2685     2702      +17     
  Branches      310      314       +4     
==========================================
+ Hits         2446     2458      +12     
- Misses        167      169       +2     
- Partials       72       75       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @fsbraun - I've reviewed your changes and they look great!

Here's what I looked at during the review
  • 🟢 General issues: all looks good
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟢 Complexity: all looks good
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@fsbraun
Copy link
Member Author

fsbraun commented Jan 15, 2025

@krosefield Can you install this branch to check if it works to your expectations? I'll be adding some tests in the meantime.

@django-cms django-cms deleted a comment from sourcery-ai bot Jan 15, 2025
@fsbraun
Copy link
Member Author

fsbraun commented Jan 15, 2025

@sourcery-ai review

Copy link

sourcery-ai bot commented Jan 15, 2025

Reviewer's Guide by Sourcery

This pull request reintroduces the functionality to delete page translations when the DJANGOCMS_ALLOW_DELETING_VERSIONS setting is enabled. This addresses issue #442 by restoring the "Delete Translation" menu in the page toolbar. Additionally, the logic for copying plugins across languages has been improved to ensure the correct page content is used.

Sequence diagram for deleting a page translation

sequenceDiagram
    actor User
    participant TB as Toolbar
    participant LM as Language Menu
    participant DM as Delete Menu
    participant PC as PageContent

    User->>TB: Access page toolbar
    TB->>LM: Open language menu
    alt DJANGOCMS_ALLOW_DELETING_VERSIONS is True && CMS version > 4.1.4
        LM->>DM: Show 'Delete Translation' submenu
        User->>DM: Select language to delete
        DM->>PC: Delete page content for selected language
        PC-->>User: Redirect to preview/refresh page
    else
        LM-->>User: Delete Translation menu not shown
    end
Loading

Class diagram for versioning toolbar components

classDiagram
    class VersioningToolbar {
        +change_language_menu()
    }
    class PageContent {
        +language: str
        +pk: int
        +delete()
    }
    class Page {
        +get_admin_content(language)
        +get_languages()
    }

    VersioningToolbar --> Page
    Page --> PageContent

    note for VersioningToolbar "Added delete translation functionality"
    note for Page "Enhanced content retrieval for language operations"
Loading

Flow diagram for translation deletion decision process

graph TD
    A[Check Delete Translation Request] --> B{DJANGOCMS_ALLOW_DELETING_VERSIONS?}
    B -->|Yes| C{CMS version > 4.1.4?}
    B -->|No| E[Hide Delete Option]
    C -->|Yes| D[Show Delete Translation Menu]
    C -->|No| E
    D --> F{Multiple translations?}
    F -->|Yes| G[Enable Delete Option]
    F -->|No| H[Disable Delete Option]
Loading

File-Level Changes

Change Details Files
Restored "Delete Translation" menu when DJANGOCMS_ALLOW_DELETING_VERSIONS is True
  • Added a conditional check for ALLOW_DELETING_VERSIONS and CMS_SUPPORTS_DELETING_TRANSLATIONS to control the visibility of the "Delete Translation" menu.
  • Added tests to verify the presence and functionality of the "Delete Translation" menu.
  • Updated the change_language_menu function to include the "Delete Translation" menu when the feature flag is enabled.
  • Ensured that the correct page content is used when copying plugins across languages to avoid potential inconsistencies or errors.
  • Added a check for CMS version compatibility to ensure that the delete translation feature is only available for supported versions.
djangocms_versioning/cms_toolbars.py
tests/test_toolbars.py
Improved plugin copying logic
  • Modified the change_language_menu function to retrieve the correct page content for copying plugins across languages.
  • Used self.page.get_admin_content(language=code) instead of self.get_page_content(language=code) to obtain the correct page content for copying plugins across languages.
djangocms_versioning/cms_toolbars.py
Added packaging dependency
  • Added the packaging library to the project dependencies to support version checking for CMS compatibility. This ensures that the delete translation feature is only enabled for compatible CMS versions.
setup.py

Assessment against linked issues

Issue Objective Addressed Explanation
#442 Reintroduce a function 'Language' > 'Delete Translation' that deletes individual language versions
#442 Allow deletion of added page translations in Django CMS 4

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time. You can also use
    this command to specify where the summary should be inserted.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @fsbraun - I've reviewed your changes and they look great!

Here's what I looked at during the review
  • 🟡 General issues: 1 issue found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟢 Complexity: all looks good
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

djangocms_versioning/cms_toolbars.py Outdated Show resolved Hide resolved
tests/test_toolbars.py Show resolved Hide resolved
tests/test_toolbars.py Show resolved Hide resolved
@filipweidemann
Copy link

@fsbraun Hey. I tested this PR together with @krosefield.

First off: the original change request is implemented and works. We now see a "Delete Translations" menu entry and can successfully delete page translations.

However, there is one edge case that might need further refinement. Let me explain:
When deleting a page translation we successfully remove the latest Version of that page in the selected language. After doing so we might be left in a weird state though.

Assume a page with translations in DE & EN, with a versioning history and multiple existing (and previously published) version objects; let's call them versions A, B, C. They were created in that order, all have been published.

Deleting the english page only removes the latest version C. We would expect that the entire versioning history of that page would get deleted as well. We do understand that this is up to debate though, so this is rather our view and can be discarded if you don't agree with us on this one.

But there is a second issue which I can only describe as "inconsistent state" when following the above procedure. Since version C was deleted, we are now left with B as the latest version. Inside the pagecontent admin we also see that this page is marked as "published" with said version. Accessing the German page yields an interesting error: we can't retrieve the english version of the page! Our language switch seems to be unaware of that page from now on, almost as if the page tree is getting disrupted there.

The page DOES exist, but it seems like the PageUrl object gets cleared when deleting the page and not created again when falling back to a previous version. I can verify this by clicking the "eye / show page" button inside the admin, entering the page that correctly exists, and then trying to edit the page settings through the cms toolbar. I am greeted with a "DoesNotExist" error page:

swappy-20250115_140625

This seems like a bug to me, the only viable thing you can do to resolve this as a user is to unpublish the english page, delete all versions and recreate the page from the German one.

This was a rather lengthy text, sorry for that, but please ping me if you need any more information. And thank you very much for working on this feature!

@fsbraun
Copy link
Member Author

fsbraun commented Jan 15, 2025

@filipweidemann Thanks for the quick reply. I need to think about deleting all versions. That's a great point. (And maybe the reason it was removed.)

Concerning your second issue, I believe this is an instance of #441 which was solved in #440. Having a page without published or draft states could shadow other pages/languages.

@filipweidemann
Copy link

@fsbraun The way I see it is as follows: you have language-scoped pages, each of them with their own version history. That is fine, maybe we have diverging content for some languages, maybe they are maintained by different teams of people, ...

However, when I decide to delete a whole language, I would expect that not only the current version is removed but rather the whole language including versions.

I would expect this simply because in my mind, a page consists of many different languages and their contents, and each language has their own contents (and versions). This almost looks like a graph to me. When deleting a language, I would expect their leaf nodes (versions) to be gone as well. But again, think about it, take your time, this is not necessarily the correct approach for this project. I am in no position to judge this.

Regarding the error: #440 was merged already, I would expect this to already be present in your feature branch, no?

And what about the PageUrl error, do you think this is also related to #441?
I am not sure how this would be fixed by #440 tbh...

Also, why would #441 even trigger here? The admin view / page content view clearly tells me that the english page I just deleted is still published. I think this is the other way round, maybe it just looks like #441 because the PageUrl is gone, therefore the language switcher only outputs one language (DE, the one that still exists / still has a PageUrl object).

Or am I missing something here?

@filipweidemann
Copy link

Oh nvm, you just merged it 3 hours ago. Any chance to update this branch to include #440 so I can test this again? Maybe it does fix it after all..

@fsbraun fsbraun marked this pull request as draft January 15, 2025 19:20
@fsbraun
Copy link
Member Author

fsbraun commented Jan 15, 2025

@filipweidemann I see your point. After looking into this, I believe this needs to be fixed additionally in the django CMS core:

  • The delete confirmation message is a bit garbled
  • The code needs to delete all content objects for a page-language combination.
    I'm afraid, this is not quite such a quick fix as I anticipated.

Next step: Fix translation deleting in the core.

@filipweidemann
Copy link

@fsbraun thanks for the update! If you need our help testing this again when it is done, just ping us.

@fsbraun
Copy link
Member Author

fsbraun commented Jan 16, 2025

Thank you, @filipweidemann ! django-cms/django-cms#8111 will fix the delete language problem for the upcoming django CMS 4.2. We'll need a backport to django CMS 4.1, too. Once I have that, I will ping you.

@fsbraun
Copy link
Member Author

fsbraun commented Jan 16, 2025

@filipweidemann You could test against this branch and this fix for the core: django-cms/django-cms#8112

Once I get your feedback I will push a piece of code that will deactivate the Delete Translations menu for versions of django CMS that do not correctly delete all versions the page contents. (This will make the menu disappear again until versions are bumped.)

@filipweidemann
Copy link

@fsbraun we did. Same errors are still present. I'd like to double-down on my hint that a cascaded deletion of the language versions' PageUrl is the root cause here.

Let me walk through this with some screenshots.
This is an excerpt from one of our djangoCMS 4 page content view in German & English. We will be focusing on the "Movies" page.
initial-en
initial-de

As you can see, nothing too fancy here. Let me also show you the initial state of the PageUrl instances for that page:
page-urls-before-deletion

Let's try to delete the English language by creating a new version of the German one (we have to do that to enable the edit controls) and then using the new menu controls. We are greeted with this prompt, asking us to confirm:
deleting-en-prompt

As you can see, not only are the PageContent, Version & StateTracking instances removed, but also the PageUrl of the English page! We can confirm this by doing a query again:
page-url-after-deletion

The publishing state of our page inside the admin view now looks like this:
page-state-after-deletion

The page is unpublished, so that's good. But the PageUrl is still missing. What if we manually "restore" the last version of that English page? We now get a green "published" bubble but there is still no PageUrl being created.

We can access the page though, either by clicking on "view" inside the admin or using the language tabs in the frontend. When we do that, the language chooser will still only display the German language, even when we are obviously on an English page! When we try to edit the pages settings through the frontend editing, we are greeted with this error, confirming that the missing PageUrl is probably not the desired state:
error-when-editing-page-settings-in-frontend

All of this can only be replicated when deleting a language whose PageContent has more than one version. If we just have a single version, this version will be deleted, showing us that the page does not exist, and creating one from the German version yields the correct results.

@fsbraun
Copy link
Member Author

fsbraun commented Jan 16, 2025

Hi @filipweidemann !

Have you installed both #443 and django-cms/django-cms#8112 ?

If a page content object exists in a language, a page url object must exist.

Deleting a translation implies deleting all page content objects and the page url object. So, if you delete, say, the German translation, the German page url object should go. In the page tree the page should be visible as "not-existing", (an empty circle), not unpublished. The fix in the django-cms package should make sure that (besides the page url) all page content objects are removed.

I have not been able to reproduce the DoesNotExist error. If I follow your steps, my results deviate in that I do not see an unpublished German content after deleting the German translation.

@filipweidemann
Copy link

@fsbraun I manage my deps using pipenv so this is my currently active lockfile (omitting other deps):

"django-cms": {
    "git": "https://github.com/fsbraun/django-cms",
    "markers": "python_version >= '3.8'",
    "ref": "18c58a16fd29386df736e165b72b062c12f538ac"
},
"djangocms-versioning": {
    "git": "https://github.com/django-cms/djangocms-versioning",
    "ref": "1c11746b655fa189a36b8637adee75a70d239556"
},

This was followed by a docker compose build && docker compose up to assure that the deps are correctly installed instead of just reusing an already existing container.

I locked & rebuilt everything again, just to be safe. But I still get this behaviour. Any ideas?

@fsbraun
Copy link
Member Author

fsbraun commented Jan 16, 2025

Yeah... It seems I didn't push everything 🤦‍♂️. The ref for django-cms should be a2da84d8f8b33dc7f1b63b2fb8ce91512227b222. Sorry for the oversight.

@filipweidemann
Copy link

Heh this would explain it I guess :D
Let me try again...

@filipweidemann
Copy link

@fsbraun okay good news: it looks like this works now! I now get a whole bunch of PageContent objects being deleted when removing the site, not just one like before. And this is correctly reflected inside the admin.

This is great tbh. I think this is done! Thanks for your effort on this!

@fsbraun fsbraun requested a review from a team January 16, 2025 17:36
@fsbraun fsbraun marked this pull request as ready for review January 16, 2025 17:37
Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @fsbraun - I've reviewed your changes and they look great!

Here's what I looked at during the review
  • 🟡 General issues: 1 issue found
  • 🟢 Security: all looks good
  • 🟡 Testing: 1 issue found
  • 🟢 Complexity: all looks good
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

djangocms_versioning/cms_toolbars.py Show resolved Hide resolved
"""
from djangocms_versioning import cms_toolbars

with patch.object(cms_toolbars, "ALLOW_DELETING_VERSIONS", True):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): Test edge case where ALLOW_DELETING_VERSIONS is True but CMS_SUPPORTS_DELETING_TRANSLATIONS is False.

It would be beneficial to add a test case that specifically checks the behavior when ALLOW_DELETING_VERSIONS is True, but the CMS version is older than 4.1.5 (where CMS_SUPPORTS_DELETING_TRANSLATIONS would be False). This ensures that the delete translation option is not offered in unsupported CMS versions even if the setting is enabled.

Suggested implementation:

    def test_change_language_menu_page_toolbar_including_delete(self):
        """Check that patched PageToolbar.change_language_menu also provides
        Delete Translation links if DJANGOCMS_ALLOW_DELETING_VERSIONS is True.
        """
        from djangocms_versioning import cms_toolbars

        with patch.object(cms_toolbars, "ALLOW_DELETING_VERSIONS", True):

    def test_change_language_menu_page_toolbar_no_delete_unsupported_cms(self):
        """Check that Delete Translation links are not shown when ALLOW_DELETING_VERSIONS
        is True but CMS version does not support deleting translations (pre-4.1.5).
        """
        from djangocms_versioning import cms_toolbars

        with patch.object(cms_toolbars, "ALLOW_DELETING_VERSIONS", True), \
             patch("cms.utils.conf.get_cms_setting") as mock_get_cms_setting:
            # Simulate CMS < 4.1.5 where CMS_SUPPORTS_DELETING_TRANSLATIONS would be False
            mock_get_cms_setting.return_value = False
            version = PageVersionFactory(content__language="en")
            PageContentWithVersionFactory(page=version.content.page, language="de")

            request = self.get_page_request(version.content.page, self.superuser)
            toolbar = CMSToolbar(request)
            toolbar.populate()

            # Get the language menu items
            page_menu = toolbar.get_menu('page')
            language_menu = page_menu.find_items(LanguageMenu)[0].item

            # Verify no delete options are present in the menu
            menu_items = language_menu.get_items()
            delete_items = [item for item in menu_items if 'delete-translation' in str(item.url)]
            self.assertEqual(len(delete_items), 0)

You may need to:

  1. Import additional dependencies at the top of the file (CMSToolbar, LanguageMenu)
  2. Adjust the test setup based on your specific test case base class and available helper methods
  3. Modify the assertions based on your specific menu structure and URL patterns

tests/test_toolbars.py Show resolved Hide resolved
tests/test_toolbars.py Show resolved Hide resolved
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feat] Allow to delete added page translation again in Django CMS 4
2 participants