def test_plugin_rendering(self): add_plugin( self.placeholder, 'PicturePlugin', language=self.language, template='default', file_grouper=self.image_grouper, use_no_cropping=True, ) with self.login_user_context(self.superuser): response = self.client.get(get_object_preview_url(self.placeholder.source, self.language)) self.assertContains(response, self.image.url) draft_image = self.create_image_obj( original_filename='draft-image.jpg', folder=self.folder, grouper=self.image_grouper, publish=False, ) with self.login_user_context(self.superuser): response = self.client.get(get_object_preview_url(self.placeholder.source, self.language)) self.assertContains(response, draft_image.url)
def test_plugin_rendering(self): """Test plugin rendering for published file""" add_plugin( self.placeholder, 'FilePlugin', language=self.language, template='default', file_grouper=self.file_grouper, ) response = self.client.get( get_object_preview_url(self.placeholder.source, self.language)) self.assertContains(response, self.file.url) draft_file = self.create_file_obj( original_filename='draft.txt', folder=self.folder, grouper=self.file_grouper, publish=False, ) response = self.client.get( get_object_preview_url(self.placeholder.source, self.language)) self.assertContains(response, draft_file.url)
def test_success_url_for_cms_wizard(self): from cms.cms_wizards import cms_page_wizard, cms_subpage_wizard from cms.toolbar.utils import get_object_preview_url from djangocms_versioning.test_utils.polls.cms_wizards import poll_wizard # Test against page creations in different languages. version = PageVersionFactory(content__language="en") self.assertEqual( cms_page_wizard.get_success_url(version.content.page, language="en"), get_object_preview_url(version.content), ) version = PageVersionFactory(content__language="en") self.assertEqual( cms_subpage_wizard.get_success_url(version.content.page, language="en"), get_object_preview_url(version.content), ) version = PageVersionFactory(content__language="de") self.assertEqual( cms_page_wizard.get_success_url(version.content.page, language="de"), get_object_preview_url(version.content, language="de"), ) # Test against a model that doesn't have a PlaceholderRelationField version = PollVersionFactory() self.assertEqual( poll_wizard.get_success_url(version.content), version.content.get_absolute_url(), )
def test_unpublish_dependencies(self, mocked_references): request = RequestFactory().get("/") version = factories.PageVersionFactory() polls = factories.PollContentFactory.create_batch(2) parent = factories.ParentFactory() child = factories.ChildFactory(parent=parent) mocked_references.return_value = [ PollContent.objects.all(), # this has 2 polls Child.objects.none(), # this is an empty queryset Parent.objects.all(), # this has 1 parent ] html = cms_config.unpublish_dependencies(request, version) mocked_references.assert_called_once_with(version.content, draft_and_published=True) # NOTE: This is not an extensive test of the html, but testing for # exact html will likely be a pain later (making this test # break easily and make it difficult to maintain) self.assertIn(get_object_preview_url(polls[0]), html) self.assertIn(get_object_preview_url(polls[1]), html) self.assertIn(get_object_preview_url(parent), html) # we passed an empty Child queryset so this should not have made # it into the html self.assertNotIn(get_object_preview_url(child), html)
def _get_instance_request(self, instance, user, path=None, edit=False, preview=False, structure=False, lang_code='en', disable=False): if not path: if edit: path = get_object_edit_url(instance) elif preview: path = get_object_preview_url(instance) elif structure: path = get_object_structure_url(instance) else: path = instance.get_absolute_url() request = RequestFactory().get(path) request.session = {} request.user = user request.LANGUAGE_CODE = lang_code request.GET = QueryDict('', mutable=True) if edit: request.GET['edit'] = None else: request.GET['edit_off'] = None if disable: request.GET[get_cms_setting('CMS_TOOLBAR_URL__DISABLE')] = None return request
def notify_version_author_version_unlocked(version, unlocking_user): # If the unlocking user is the current author, don't send a notification email if version.created_by == unlocking_user: return # If the users name is available use it, otherwise use their username username = unlocking_user.get_full_name() or unlocking_user.username site = get_current_site() recipients = [version.created_by.email] subject = "[Django CMS] ({site_name}) {title} - {description}".format( site_name=site.name, title=version.content, description=_("Unlocked"), ) version_url = get_absolute_url(get_object_preview_url(version.content)) # Prepare and send the email template_context = { 'version_link': version_url, 'by_user': username, } status = send_email( recipients=recipients, subject=subject, template='unlock-notification.txt', template_context=template_context, ) return status
def test_notify_version_author_version_unlocked_email_sent_for_different_user( self): """ The user unlocking a version that is authored buy a different user should be sent a notification email """ draft_version = factories.PageVersionFactory( content__template="", created_by=self.user_author) draft_unlock_url = self.get_admin_url( self.versionable.version_model_proxy, 'unlock', draft_version.pk) # Check that no emails exist self.assertEqual(len(mail.outbox), 0) # Unlock the version with a different user with unlock permissions with self.login_user_context(self.user_has_unlock_perms): self.client.post(draft_unlock_url, follow=True) site = get_current_site() expected_subject = "[Django CMS] ({site_name}) {title} - {description}".format( site_name=site.name, title=draft_version.content, description=_("Unlocked"), ) expected_body = "The following draft version has been unlocked by {by_user} for their use.".format( by_user=self.user_has_unlock_perms) expected_version_url = get_absolute_url( get_object_preview_url(draft_version.content)) self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].subject, expected_subject) self.assertEqual(mail.outbox[0].to[0], self.user_author.email) self.assertTrue(expected_body in mail.outbox[0].body) self.assertTrue(expected_version_url in mail.outbox[0].body)
def test_plugin_rendering(self): parent_plugin = add_plugin( self.placeholder, 'VideoPlayerPlugin', language=self.language, template='default', ) add_plugin( self.placeholder, 'VideoSourcePlugin', language=self.language, target=parent_plugin, file_grouper=self.video_file_grouper, ) video_file_grouper_2 = FileGrouper.objects.create() video_track_file_obj = self.create_file_obj( original_filename='sandstorm-subtitles.mp4', folder=self.folder, grouper=video_file_grouper_2, publish=False, ) with self.login_user_context(self.superuser): response = self.client.get( get_object_preview_url(self.placeholder.source, self.language)) self.assertContains(response, self.video_file.url) self.assertNotContains(response, video_track_file_obj.url) draft_file = self.create_file_obj( original_filename='sandstorm.mp4', folder=self.folder, grouper=self.video_file_grouper, publish=False, ) add_plugin( self.placeholder, 'VideoTrackPlugin', language=self.language, target=parent_plugin, file_grouper=video_file_grouper_2, ) with self.login_user_context(self.superuser): response = self.client.get( get_object_preview_url(self.placeholder.source, self.language)) self.assertContains(response, draft_file.url) self.assertContains(response, video_track_file_obj.url)
def get_wizard_success_url(self, obj, **kwargs): # noqa: E302 cms_extension = apps.get_app_config('djangocms_versioning').cms_extension model = obj.__class__ if cms_extension.is_content_model_versioned(model) and is_editable_model( model): language = kwargs.get('language', None) return get_object_preview_url(obj, language) return original_get_wizard_success_url(self, obj, **kwargs)
def object_preview_url(obj): """ Displays the preview url for obj. """ if isinstance(obj, (int, str)): raise template.TemplateSyntaxError( "object_preview_url tag requires a model object as argument") return get_object_preview_url(obj)
def _assert_node(self, node, version, edit_or_preview=True): content = version.content self.assertEqual(node.title, content.title) if edit_or_preview: self.assertEqual(node.url, get_object_preview_url(content)) else: self.assertEqual(node.url, content.get_absolute_url())
def _get_preview_link(self, obj, request, disabled=False): return render_to_string( "djangocms_pageadmin/admin/icons/preview.html", { "url": get_object_preview_url(obj), "disabled": disabled }, )
def test_object_preview_url_rendered_with_obj_arg(self): obj = PageContentFactory() context = Context({"obj": obj}) expected_url = get_object_preview_url(obj) template_to_render = Template("{% load djangocms_references_tags %}" "{% object_preview_url obj %}") rendered_template = template_to_render.render(context) self.assertInHTML(expected_url, rendered_template)
def test_plugin_rendering(self): parent_plugin = add_plugin( self.placeholder, 'AudioPlayerPlugin', language=self.language, template='default', ) audio_file_plugin = add_plugin( self.placeholder, 'AudioFilePlugin', language=self.language, target=parent_plugin, file_grouper=self.audio_file_grouper, ) audio_file_grouper_2 = FileGrouper.objects.create() audio_track_file_obj = self.create_file_obj( original_filename='sandstorm-subtitles.mp3', folder=self.folder, grouper=audio_file_grouper_2, publish=False, ) response = self.client.get( get_object_preview_url(self.placeholder.source, self.language)) self.assertContains(response, self.audio_file.url) self.assertNotContains(response, audio_track_file_obj.url) draft_file = self.create_file_obj( original_filename='sandstorm.ogg', folder=self.folder, grouper=self.audio_file_grouper, publish=False, ) add_plugin( self.placeholder, 'AudioTrackPlugin', language=self.language, target=audio_file_plugin, file_grouper=audio_file_grouper_2, ) response = self.client.get( get_object_preview_url(self.placeholder.source, self.language)) self.assertContains(response, draft_file.url) self.assertContains(response, audio_track_file_obj.url)
def test_preview_link(self): pagecontent = PageContentWithVersionFactory() func = self.modeladmin._list_actions(self.get_request("/")) response = func(pagecontent) soup = parse_html(response) element = soup.find("a", {"class": "cms-page-admin-action-preview"}) self.assertIsNotNone(element, "Missing a.cms-page-admin-action-preview element") self.assertEqual(element["title"], "Preview") self.assertEqual(element["href"], get_object_preview_url(pagecontent))
def test_plugin_rendering_thumbnails(self): add_plugin( self.placeholder, 'PicturePlugin', language=self.language, template='default', file_grouper=self.image_grouper, ) with self.login_user_context(self.superuser): response = self.client.get(get_object_preview_url(self.placeholder.source, self.language)) self.assertContains( response, '/media/{}/{}'.format(self.image.file.thumbnail_basedir, self.image.file.name), )
def get_preview_link(self, obj): content = obj.moderation_request.version.content if is_editable_model(content.__class__): object_preview_url = get_object_preview_url(content) else: object_preview_url = reverse( "admin:{app}_{model}_change".format( app=content._meta.app_label, model=content._meta.model_name), args=[content.pk], ) return format_html( '<a href="{}" class="js-moderation-close-sideframe" target="_top">' '<span class="cms-icon cms-icon-eye"></span>' "</a>", object_preview_url, )
def content_link(self, obj): content = obj.content # If the object is editable the preview view should be used. if is_editable_model(content.__class__): url = get_object_preview_url(content) # Or else, the standard change view should be used else: url = reverse('admin:{app}_{model}_change'.format( app=content._meta.app_label, model=content._meta.model_name, ), args=[content.pk]) return format_html( '<a target="_top" class="js-versioning-close-sideframe" href="{url}">{label}</a>', url=url, label=content, )
def test_default_cms_edit_button_is_used_for_non_versioned_model(self): """ The default cms edit button is present for a default model """ unversionedpoll = FancyPollFactory() url = get_object_preview_url(unversionedpoll) edit_url = get_object_edit_url(unversionedpoll) with self.login_user_context(self.get_superuser()): response = self.client.post(url) found_button_list = find_toolbar_buttons("Edit", response.wsgi_request.toolbar) # Only one edit button exists self.assertEqual(len(found_button_list), 1) # The only edit button that exists is the standard cms button self.assertEqual(found_button_list[0].url, edit_url)
def test_audio_folder_plugin(self): audio_player_plugin = add_plugin( self.placeholder, 'AudioPlayerPlugin', language=self.language, template='default', ) add_plugin( self.placeholder, 'AudioFolderPlugin', language=self.language, target=audio_player_plugin, audio_folder=self.folder, ) file_grouper_1 = FileGrouper.objects.create() published_nonaudio_file = self.create_file_obj( original_filename='published.txt', folder=self.folder, grouper=file_grouper_1, publish=True, ) draft_file = self.create_file_obj( original_filename='draft.mp3', folder=self.folder, grouper=self.audio_file_grouper, publish=False, ) file_grouper_2 = FileGrouper.objects.create() draft_file_2 = self.create_file_obj( original_filename='draft2.mp3', folder=self.folder, grouper=file_grouper_2, publish=False, ) with self.login_user_context(self.superuser): response = self.client.get(get_object_preview_url(self.placeholder.source, self.language)) self.assertContains(response, draft_file.url) self.assertContains(response, draft_file_2.url) self.assertNotContains(response, published_nonaudio_file.url) self.assertNotContains(response, self.audio_file.url)
def get_preview_url(content_obj): """If the object is editable the cms preview view should be used, with the toolbar. This method is provides the URL for it. """ versionable = versionables.for_content(content_obj) if versionable.preview_url: return versionable.preview_url(content_obj) if is_editable_model(content_obj.__class__): url = get_object_preview_url(content_obj) # Or else, the standard change view should be used else: url = reverse( "admin:{app}_{model}_change".format( app=content_obj._meta.app_label, model=content_obj._meta.model_name), args=[content_obj.pk], ) return url
def test_default_cms_edit_button_is_replaced_by_versioning_edit_button( self): """ The versioning edit button is available on the toolbar when versioning is installed and the model is versionable. """ pagecontent = PageVersionFactory(content__template="") url = get_object_preview_url(pagecontent.content) edit_url = self._get_edit_url(pagecontent.content, VersioningCMSConfig.versioning[0]) with self.login_user_context(self.get_superuser()): response = self.client.post(url) found_button_list = find_toolbar_buttons("Edit", response.wsgi_request.toolbar) # Only one edit button exists self.assertEqual(len(found_button_list), 1) # The only edit button that exists is the versioning button self.assertEqual(found_button_list[0].url, edit_url)
def override_language_menu(self): """ Override the default language menu for pages that are versioned. The default language menu is too generic so for pages we need to replace it. """ # Only override the menu if a page can be found if settings.USE_I18N and self.page: language_menu = self.toolbar.get_menu(LANGUAGE_MENU_IDENTIFIER, _('Language')) # remove_item uses `items` attribute so we have to copy object for _item in copy(language_menu.items): language_menu.remove_item(item=_item) for code, name in get_language_tuple(self.current_site.pk): # Get the pagw content, it could be draft too! page_content = self.get_page_content(language=code) if page_content: url = get_object_preview_url(page_content, code) language_menu.add_link_item( name, url=url, active=self.current_lang == code)
def test_plugin_rendering(self): self.client.force_login(self.superuser) folder = Folder.objects.create(name='test folder 9') add_plugin( self.placeholder, 'FolderPlugin', language=self.language, template='default', folder_src=folder, ) file_grouper_1 = FileGrouper.objects.create() published_file = self.create_file_obj( original_filename='published.txt', folder=folder, grouper=file_grouper_1, publish=True, ) draft_file = self.create_file_obj( original_filename='draft.txt', folder=folder, grouper=file_grouper_1, publish=False, ) file_grouper_2 = FileGrouper.objects.create() draft_file_2 = self.create_file_obj( original_filename='draft2.txt', folder=folder, grouper=file_grouper_2, publish=False, ) response = self.client.get( get_object_preview_url(self.placeholder.source, self.language)) self.assertContains(response, draft_file.url) self.assertContains(response, draft_file_2.url) self.assertNotContains(response, published_file.url)
def test_static_alias_shows_correct_content_for_versioning_states(self): """ The correct contents are shown when viewing the static alias: - A draft page shows draft content - A published page shows published content or nothing at all """ from djangocms_versioning.constants import PUBLISHED category = Category.objects.create( name=DEFAULT_STATIC_ALIAS_CATEGORY_NAME) alias = self._create_alias( plugins=None, name='test alias', category=category, published=True, static_code="template_example_global_alias_code") add_plugin( alias.get_placeholder(language='en'), 'TextPlugin', language='en', body='Published content for: template_example_global_alias_code', ) page = create_page(title="Static Code Test", language='en', template='static_alias.html', limit_visibility_in_menu=None, created_by=self.superuser) # Publish the page and create a draft alias self._publish(page, 'en') version = self._get_version(alias, PUBLISHED, 'en') draft = version.copy(self.superuser) # Add draft content to the draft version add_plugin( draft.content.placeholder, 'TextPlugin', language='en', body= 'Updated Draft content for: template_example_global_alias_code', ) page_content = page.get_title_obj("en") page_live_url = page.get_absolute_url() page_edit_url = get_object_edit_url(page_content, "en") page_preview_url = get_object_preview_url(page_content, "en") # The live page should still contain the published contents live_response = self.client.get(page_live_url) self.assertContains( live_response, 'Published content for: template_example_global_alias_code') self.assertNotContains( live_response, 'Updated Draft content for: template_example_global_alias_code') # The edit and preview url should show the draft contents with self.login_user_context(self.superuser): edit_response = self.client.get(page_edit_url) preview_response = self.client.get(page_preview_url) self.assertContains( edit_response, 'Updated Draft content for: template_example_global_alias_code') self.assertContains( preview_response, 'Updated Draft content for: template_example_global_alias_code')
def prepare_url(self, obj): return get_object_preview_url(obj, obj.language)
def get_nodes(self, request): site = self.renderer.site language = self.renderer.request_language pages_qs = get_page_queryset(site).select_related('node') visible_pages_for_user = get_visible_nodes(request, pages_qs, site) if not visible_pages_for_user: return [] cms_extension = apps.get_app_config( 'djangocms_versioning').cms_extension toolbar = get_toolbar_from_request(request) edit_or_preview = toolbar.edit_mode_active or toolbar.preview_mode_active menu_nodes = [] node_id_to_page = {} homepage_content = None # Depending on the toolbar mode, we need to get the correct version. # On edit or preview mode: return DRAFT, # if DRAFT does not exists then return PUBLISHED. # On public mode: return PUBLISHED. if edit_or_preview: states = [constants.DRAFT, constants.PUBLISHED] else: states = [constants.PUBLISHED] versionable_item = cms_extension.versionables_by_grouper[Page] versioned_page_contents = ( versionable_item.content_model._base_manager.filter( language=language, page__in=pages_qs, versions__state__in=states, ).order_by('page__node__path', 'versions__state').select_related( 'page', 'page__node').prefetch_related('versions')) added_pages = [] for page_content in versioned_page_contents: page = page_content.page if page not in visible_pages_for_user: # The page is restricted for the user. # Therefore we avoid adding it to the menu. continue version = page_content.versions.all()[0] if (page.pk in added_pages and edit_or_preview and version.state == constants.PUBLISHED): # Page content is already added. This is the case where you # have both draft and published and in edit/preview mode. # We give priority to draft which is already sorted by the query. # Therefore we ignore the published version. continue page_tree_node = page.node parent_id = node_id_to_page.get(page_tree_node.parent_id) if page_tree_node.parent_id and not parent_id: # If the parent page is not available, # we skip adding the menu node. continue # Construct the url based on the toolbar mode. if edit_or_preview: url = get_object_preview_url(page_content) else: url = page_content.get_absolute_url() # Create the new navigation node. new_node = CMSVersionedNavigationNode( id=page.pk, attr=_get_attrs_for_node(self.renderer, page_content), title=page_content.menu_title or page_content.title, url=url, visible=page_content.in_navigation, ) if not homepage_content: # Set the home page content. homepage_content = page_content if page.is_home else None cut_homepage = homepage_content and not homepage_content.in_navigation if cut_homepage and parent_id == homepage_content.page.pk: # When the homepage is hidden from navigation, # we need to cut all its direct children from it. new_node.parent_id = None else: new_node.parent_id = parent_id node_id_to_page[page_tree_node.pk] = page.pk menu_nodes.append(new_node) added_pages.append(page.pk) return menu_nodes
def get_absolute_url(self): return get_object_preview_url(self)
def test_change_language_menu_page_toolbar_language_selector_version_link( self): """ Ensure that the correct version is navigated to in the language selector. A real world scenario / issue seen: - Version 3: Draft - Version 2: Published - Version 1: Archived Version 1 was returned in the toolbar language selector which is incorrect, the latest version 4 should be returned. """ superuser = self.get_superuser() en_pagecontent_1 = PageContentWithVersionFactory(language="en") page = en_pagecontent_1.page de_pagecontent_1 = PageContentWithVersionFactory(page=page, language="de") # Create remaining 3 versions for it it_pagecontent_1 = PageContentWithVersionFactory( page=page, language="it", version__state=ARCHIVED) it_pagecontent_1_version = it_pagecontent_1.versions.first() # Make version 1 archived by publishing the new version 2 it_pagecontent_2 = PageContentWithVersionFactory( page=page, language="it", version__state=PUBLISHED) it_pagecontent_2_version = it_pagecontent_2.versions.first() # Create a new draft, which is what we expect to use it_pagecontent_3 = PageContentWithVersionFactory(page=page, language="it", version__state=DRAFT) it_pagecontent_3_version = it_pagecontent_3.versions.first() # Sanity check that all versions are int he state that we expect: Archived, Published, Draft self.assertEqual(it_pagecontent_1_version.state, ARCHIVED) self.assertEqual(it_pagecontent_2_version.state, PUBLISHED) self.assertEqual(it_pagecontent_3_version.state, DRAFT) page.update_languages(["en", "de", "it"]) request = self.get_page_request( page=page, path=get_object_edit_url(en_pagecontent_1), user=superuser, ) request.toolbar.set_object(en_pagecontent_1) request.toolbar.populate() request.toolbar.post_template_populate() language_menu = request.toolbar.get_menu(LANGUAGE_MENU_IDENTIFIER) language_menu_item_names = [ item.name for item in language_menu.items if hasattr(item, "name") ] self.assertIn("English", language_menu_item_names) self.assertIn("Deutsche", language_menu_item_names) self.assertIn("Italiano", language_menu_item_names) en_item = self._get_toolbar_item_by_name(language_menu, "English") en_preview_url = get_object_preview_url(en_pagecontent_1, "en") de_item = self._get_toolbar_item_by_name(language_menu, "Deutsche") de_preview_url = get_object_preview_url(de_pagecontent_1, "de") it_item = self._get_toolbar_item_by_name(language_menu, "Italiano") it_preview_url = get_object_preview_url(it_pagecontent_3, "it") # Ensure that each menu item points to the correct url self.assertEqual(en_item.url, en_preview_url) self.assertEqual(de_item.url, de_preview_url) self.assertEqual(it_item.url, it_preview_url)