Example #1
0
    def test_signals_categories_no_parent(self, mock_bulk, *_):
        """
        Publishing a category should update its document in the Elasticsearch categories
        index, and the documents for published courses to which it is related, excluding snapshots.
        """
        category = CategoryFactory()
        published_course, _unpublished_course = CourseFactory.create_batch(
            2, fill_categories=[category])
        published_course.extended_object.publish("en")
        published_course.refresh_from_db()
        self.run_commit_hooks()
        mock_bulk.reset_mock()

        category.extended_object.publish("en")
        category.refresh_from_db()

        # Elasticsearch should not be called before the db transaction is successful
        self.assertFalse(mock_bulk.called)
        self.run_commit_hooks()

        self.assertEqual(mock_bulk.call_count, 1)
        self.assertEqual(len(mock_bulk.call_args[1]["actions"]), 2)
        actions = list(mock_bulk.call_args[1]["actions"])
        self.assertEqual(
            actions[0]["_id"],
            str(published_course.public_extension.extended_object_id))
        self.assertEqual(actions[0]["_type"], "course")
        self.assertEqual(actions[1]["_id"], "L-0001")
        self.assertEqual(actions[1]["_type"], "category")
Example #2
0
    def test_signals_categories_publish(self, mock_bulk, *_):
        """
        Publishing a category should update its document in the Elasticsearch categories
        index, and the documents for published courses to which it is related, excluding snapshots.
        """
        parent = CategoryFactory(should_publish=True)
        category = CategoryFactory(page_parent=parent.extended_object)
        published_course, _unpublished_course = CourseFactory.create_batch(
            2, fill_categories=[category])
        self.assertTrue(published_course.extended_object.publish("en"))
        published_course.refresh_from_db()
        self.run_commit_hooks()
        mock_bulk.reset_mock()

        self.assertTrue(category.extended_object.publish("en"))
        category.refresh_from_db()

        # Elasticsearch should not be called before the db transaction is successful
        self.assertFalse(mock_bulk.called)
        self.run_commit_hooks()

        self.assertEqual(mock_bulk.call_count, 1)
        self.assertEqual(len(mock_bulk.call_args[1]["actions"]), 3)
        actions = list(mock_bulk.call_args[1]["actions"])
        self.assertEqual(actions[0]["_id"], published_course.get_es_id())
        self.assertEqual(actions[0]["_op_type"], "index")
        self.assertEqual(actions[0]["_index"], "test_courses")
        self.assertEqual(actions[1]["_id"], category.get_es_id())
        self.assertEqual(actions[1]["_op_type"], "index")
        self.assertEqual(actions[1]["_index"], "richie_categories")
        self.assertEqual(actions[2]["_id"], parent.get_es_id())
        self.assertEqual(actions[2]["_op_type"], "index")
        self.assertEqual(actions[2]["_index"], "richie_categories")
Example #3
0
    def test_signals_categories_move(self, mock_bulk, *_):
        """
        Make sure all categories are re-indexed when a category page is moved
        in the CMS page tree.
        """
        user = UserFactory(is_staff=True, is_superuser=True)
        # Create a hierarchy of courses, with 2 parents to move the child between them
        grandparent = CategoryFactory(page_reverse_id="subjects",
                                      should_publish=True)
        parent_1 = CategoryFactory(page_parent=grandparent.extended_object,
                                   should_publish=True)
        parent_2 = CategoryFactory(page_parent=grandparent.extended_object,
                                   should_publish=True)
        child = CategoryFactory(page_parent=parent_1.extended_object,
                                should_publish=True)
        # Create a course so we can verify it's not re-indexed when the linked category is moved
        CourseFactory(fill_categories=[child])
        self.assertEqual(child.extended_object.parent_page.id,
                         parent_1.extended_object.id)

        self.client.force_login(user)
        self.client.post(
            f"/en/admin/cms/page/{child.extended_object.id}/move-page/",
            {
                "position": "0",
                "id": str(child.extended_object.id),
                "target": str(parent_2.extended_object.id),
                "site": "1",
            },
        )

        child.refresh_from_db()
        self.assertEqual(child.extended_object.parent_page.id,
                         parent_2.extended_object.id)
        self.assertEqual(mock_bulk.call_count, 1)
        actions = [*mock_bulk.call_args[1]["actions"]]
        self.assertEqual(len(actions), 4)
        self.assertEqual(actions[0]["_id"],
                         str(child.public_extension.extended_object_id))
        self.assertEqual(actions[0]["_index"], "richie_categories")
        self.assertEqual(actions[0]["_op_type"], "index")
        self.assertEqual(actions[1]["_id"],
                         str(parent_2.public_extension.extended_object_id))
        self.assertEqual(actions[1]["_index"], "richie_categories")
        self.assertEqual(actions[1]["_op_type"], "index")
        self.assertEqual(actions[2]["_id"],
                         str(parent_1.public_extension.extended_object_id))
        self.assertEqual(actions[2]["_index"], "richie_categories")
        self.assertEqual(actions[2]["_op_type"], "index")
        self.assertEqual(actions[3]["_id"],
                         str(grandparent.public_extension.extended_object_id))
        self.assertEqual(actions[3]["_index"], "richie_categories")
        self.assertEqual(actions[3]["_op_type"], "index")
Example #4
0
    def test_signals_categories_unpublish(self, mock_bulk, *_):
        """
        Unpublishing a category in one language should update its document in the Elasticsearch
        categories index or delete it if there is no language published anymore.
        It should also update the documents for published courses to which it is related
        excluding snapshots.
        """
        category = CategoryFactory(page_languages=["en", "fr"],
                                   should_publish=True)
        published_course, _unpublished_course = CourseFactory.create_batch(
            2, fill_categories=[category])
        self.assertTrue(published_course.extended_object.publish("en"))
        published_course.refresh_from_db()
        self.run_commit_hooks()
        mock_bulk.reset_mock()

        # - Unpublish the first language
        self.assertTrue(category.extended_object.unpublish("en"))
        category.refresh_from_db()

        # Elasticsearch should not be called before the db transaction is successful
        self.assertFalse(mock_bulk.called)
        self.run_commit_hooks()

        self.assertEqual(mock_bulk.call_count, 1)
        self.assertEqual(len(mock_bulk.call_args[1]["actions"]), 2)
        actions = list(mock_bulk.call_args[1]["actions"])
        self.assertEqual(actions[0]["_id"], published_course.get_es_id())
        self.assertEqual(actions[0]["_op_type"], "index")
        self.assertEqual(actions[0]["_index"], "test_courses")
        self.assertEqual(actions[1]["_id"], category.get_es_id())
        self.assertEqual(actions[1]["_op_type"], "index")
        self.assertEqual(actions[1]["_index"], "richie_categories")

        mock_bulk.reset_mock()

        # - Unpublish the second language
        self.assertTrue(category.extended_object.unpublish("fr"))
        category.refresh_from_db()

        # Elasticsearch should not be called before the db transaction is successful
        self.assertFalse(mock_bulk.called)
        self.run_commit_hooks()

        self.assertEqual(mock_bulk.call_count, 1)
        self.assertEqual(len(mock_bulk.call_args[1]["actions"]), 2)
        actions = list(mock_bulk.call_args[1]["actions"])
        self.assertEqual(actions[0]["_id"], published_course.get_es_id())
        self.assertEqual(actions[0]["_op_type"], "index")
        self.assertEqual(actions[0]["_index"], "test_courses")
        self.assertEqual(actions[1]["_id"], category.get_es_id())
        self.assertEqual(actions[1]["_op_type"], "delete")
        self.assertEqual(actions[1]["_index"], "richie_categories")
    def test_cms_plugins_category_fallback_when_never_published(self):
        """
        The category plugin should render in the fallback language when the category
        page has never been published in the current language.
        """
        # Create a category
        category = CategoryFactory(
            page_title={"en": "public title", "fr": "titre public"},
            fill_logo={"original_filename": "logo.jpg", "default_alt_text": "my logo"},
        )
        category_page = category.extended_object

        # Create a page to add the plugin to
        page = create_i18n_page({"en": "A page", "fr": "Une page"})
        placeholder = page.placeholders.get(slot="maincontent")
        add_plugin(placeholder, CategoryPlugin, "en", **{"page": category_page})
        add_plugin(placeholder, CategoryPlugin, "fr", **{"page": category_page})

        # Publish only the French version of the category
        category_page.publish("fr")

        # Check the page content in English
        page.publish("en")
        url = page.get_absolute_url(language="en")
        response = self.client.get(url)

        # The english category's name should be present as a link to the cms page
        # But the locale in the url should remain "en"
        self.assertIn(
            '<a class="category-glimpse" href="/en/titre-public/"',
            re.sub(" +", " ", str(response.content).replace("\\n", "")),
        )

        # The category's title should be wrapped in a p
        category.refresh_from_db()
        self.assertContains(
            response,
            '<h2 class="category-glimpse__title">titre public</h2>',
            html=True,
        )
        self.assertNotContains(response, "public title")

        # category's cover should be present
        pattern = (
            r'<div class="category-glimpse__logo">'
            r'<img src="/media/filer_public_thumbnails/filer_public/.*logo\.jpg__200x200'
            r'.*alt=""'
        )
        self.assertIsNotNone(re.search(pattern, str(response.content)))
Example #6
0
    def test_admin_category_change_view_post(self):
        """Validate that the category can be updated via the admin."""
        user = UserFactory(is_staff=True, is_superuser=True)
        self.client.login(username=user.username, password="******")

        # Create a category
        category = CategoryFactory()

        # Get the admin change view
        url = reverse("admin:courses_category_change", args=[category.id])
        data = {"color": "#c71414"}
        response = self.client.post(url, data)
        self.assertEqual(response.status_code, 302)

        # Check that the category was updated as expected
        category.refresh_from_db()
        self.assertEqual(category.color, "#c71414")
    def test_cms_plugins_organizations_by_category_render_on_public_page(self):
        """
        The organizations by category plugin should render as expected on a public page.
        """
        # Create a category
        category = CategoryFactory(
            page_title={"en": "category title", "fr": "titre catégorie"}
        )
        category_page = category.extended_object

        # Create organizations
        published_organization = OrganizationFactory(
            page_title={"en": "public title", "fr": "titre public"},
            fill_categories=[category],
            fill_logo={"original_filename": "logo.jpg"},
            should_publish=True,
        )
        OrganizationFactory(
            page_title={"en": "private title", "fr": "titre privé"},
            fill_categories=[category],
            fill_logo={"original_filename": "logo.jpg"},
        )

        # Create a page to add the plugin to
        page = create_i18n_page({"en": "A page", "fr": "Une page"})
        placeholder = page.placeholders.get(slot="maincontent")
        add_plugin(
            placeholder, OrganizationsByCategoryPlugin, "en", **{"page": category_page}
        )
        add_plugin(
            placeholder, OrganizationsByCategoryPlugin, "fr", **{"page": category_page}
        )

        category_page.publish("en")
        category_page.publish("fr")
        category.refresh_from_db()

        page.publish("en")
        page.publish("fr")

        # Check the page content in English
        url = page.get_absolute_url(language="en")

        # The plugin should not be visible on the public page before it is published
        category_page.unpublish("en")
        response = self.client.get(url)
        self.assertNotContains(response, "public title")

        # # Republish the plugin
        category_page.publish("en")

        # Now modify the organization to have a draft different from the public version
        title_obj = published_organization.extended_object.get_title_obj(language="en")
        title_obj.title = "draft title"
        title_obj.save()

        # Publishing the page again should make the plugin public
        page.publish("en")

        # Check the page content in English
        response = self.client.get(url)
        # The organization's name should be present as a link to the cms page
        # And CMS page title should be in title attribute of the link
        self.assertIn(
            '<a class="organization-glimpse" '
            'href="/en/public-title/" title="public title"',
            re.sub(" +", " ", str(response.content).replace("\\n", "")),
        )

        # The organization's title should be wrapped in a div
        self.assertContains(
            response,
            '<div class="organization-glimpse__title">{:s}</div>'.format(
                published_organization.public_extension.extended_object.get_title()
            ),
            html=True,
        )
        self.assertNotContains(response, "draft")
        self.assertNotContains(response, "private")

        # Organization's logo should be present
        pattern = (
            r'<div class="organization-glimpse__logo">'
            r'<img src="/media/filer_public_thumbnails/filer_public/.*logo\.jpg__200x113'
            r'.*alt=""'
        )
        self.assertIsNotNone(re.search(pattern, str(response.content)))

        # Same checks in French
        url = page.get_absolute_url(language="fr")
        response = self.client.get(url)
        self.assertIn(
            '<a class="organization-glimpse" '
            'href="/fr/titre-public/" title="titre public"',
            re.sub(" +", " ", str(response.content).replace("\\n", "")),
        )
        pattern = (
            r'<div class="organization-glimpse__logo">'
            r'<img src="/media/filer_public_thumbnails/filer_public/.*logo\.jpg__200x113'
            r'.*alt=""'
        )
        self.assertIsNotNone(re.search(pattern, str(response.content)))
    def test_cms_plugins_category_render_on_public_page(self):
        """
        The category plugin should render as expected on a public page.
        """
        # Create a Category
        category = CategoryFactory(
            page_title={"en": "public title", "fr": "titre publique"},
            fill_logo={"original_filename": "logo.jpg", "default_alt_text": "my logo"},
        )
        category_page = category.extended_object

        # Create a page to add the plugin to
        page = create_i18n_page({"en": "A page", "fr": "Une page"})
        placeholder = page.placeholders.get(slot="maincontent")
        add_plugin(placeholder, CategoryPlugin, "en", **{"page": category_page})
        add_plugin(placeholder, CategoryPlugin, "fr", **{"page": category_page})

        category_page.publish("en")
        category_page.publish("fr")
        category.refresh_from_db()

        page.publish("en")
        page.publish("fr")

        url = page.get_absolute_url(language="en")

        # The category plugin should not be visible on the public page before it is published
        category_page.unpublish("en")
        response = self.client.get(url)
        self.assertNotContains(response, "public title")

        # Republish the plugin
        category_page.publish("en")

        # Now modify the category to have a draft different from the public version
        title_obj = category_page.get_title_obj(language="en")
        title_obj.title = "draft title"
        title_obj.save()

        # Publishing the page again should make the plugin public
        page.publish("en")

        # Check the page content in English
        response = self.client.get(url)

        # Category's title should be present as a link to the cms page
        # And CMS page title should be in title attribute of the link
        self.assertContains(
            response,
            '<a class="category-glimpse" href="/en/public-title/"',
            status_code=200,
        )
        # The category's title should be wrapped in a div
        self.assertContains(
            response,
            '<h2 class="category-glimpse__title">{:s}</h2>'.format(
                category.public_extension.extended_object.get_title()
            ),
            html=True,
        )
        self.assertNotContains(response, "draft title")

        # Category's logo should be present
        pattern = (
            r'<div class="category-glimpse__logo">'
            r'<img src="/media/filer_public_thumbnails/filer_public/.*logo\.jpg__200x200'
            r'.*alt=""'
        )
        self.assertIsNotNone(re.search(pattern, str(response.content)))

        # Same checks in French
        url = page.get_absolute_url(language="fr")
        response = self.client.get(url)
        self.assertContains(
            response,
            '<a class="category-glimpse" href="/fr/titre-publique/"',
            status_code=200,
        )
        pattern = (
            r'<div class="category-glimpse__logo">'
            r'<img src="/media/filer_public_thumbnails/filer_public/.*logo\.jpg__200x200'
            r'.*alt=""'
        )
        self.assertIsNotNone(re.search(pattern, str(response.content)))
Example #9
0
    def test_cms_plugins_category_render_on_public_page(self):
        """
        The category plugin should render as expected on a public page.
        """
        # Create a filer fake image
        image = FilerImageFactory()

        # Create a Category
        category = CategoryFactory(page_title={
            "en": "public title",
            "fr": "titre publique"
        })
        category_page = category.extended_object

        # Add logo to related placeholder
        logo_placeholder = category_page.placeholders.get(slot="logo")
        add_plugin(
            logo_placeholder, PicturePlugin, "en", **{
                "picture": image,
                "attributes": {
                    "alt": "logo description"
                }
            })
        add_plugin(
            logo_placeholder, PicturePlugin, "fr", **{
                "picture": image,
                "attributes": {
                    "alt": "description du logo"
                }
            })

        # Create a page to add the plugin to
        page = create_i18n_page({"en": "A page", "fr": "Une page"})
        placeholder = page.placeholders.get(slot="maincontent")
        add_plugin(placeholder, CategoryPlugin, "en",
                   **{"page": category_page})
        add_plugin(placeholder, CategoryPlugin, "fr",
                   **{"page": category_page})

        category_page.publish("en")
        category_page.publish("fr")
        category.refresh_from_db()

        page.publish("en")
        page.publish("fr")

        url = page.get_absolute_url(language="en")

        # The category plugin should not be visible on the public page before it is published
        category_page.unpublish("en")
        response = self.client.get(url)
        self.assertNotContains(response, "public title")

        # Republish the plugin
        category_page.publish("en")

        # Now modify the category to have a draft different from the public version
        title_obj = category_page.get_title_obj(language="en")
        title_obj.title = "draft title"
        title_obj.save()

        # Publishing the page again should make the plugin public
        page.publish("en")

        # Check the page content in English
        response = self.client.get(url)
        # Category's title should be present as a link to the cms page
        # And CMS page title should be in title attribute of the link
        self.assertContains(
            response,
            '<a class="category-plugin__body" href="/en/public-title/" title="{title:s}"'
            .format(
                title=category.public_extension.extended_object.get_title()),
            status_code=200,
        )
        # The category's title should be wrapped in a div
        self.assertContains(
            response,
            '<div class="category-plugin__title">{:s}</div>'.format(
                category.public_extension.extended_object.get_title()),
            html=True,
        )
        self.assertNotContains(response, "draft title")

        # Category's logo should be present
        # pylint: disable=no-member
        self.assertContains(response, image.file.name)

        # Same checks in French
        url = page.get_absolute_url(language="fr")
        response = self.client.get(url)
        self.assertContains(
            response,
            '<a class="category-plugin__body" href="/fr/titre-publique/" title="{title:s}"'
            .format(
                title=category.public_extension.extended_object.get_title()),
            status_code=200,
        )
        # pylint: disable=no-member
        self.assertContains(response, image.file.name)