Example #1
0
    def test_is_multipart(self):
        """
        Check whether is_multipart returns True when an InlinePanel contains
        a FileInput and False otherwise
        """
        SpeakerObjectList = ObjectList([
            InlinePanel('speakers', label="Speakers", panels=[
                FieldPanel('first_name', widget=forms.FileInput),
            ]),
        ]).bind_to_model(EventPage)
        SpeakerInlinePanel = SpeakerObjectList.children[0]
        EventPageForm = SpeakerObjectList.get_form_class(EventPage)

        event_page = EventPage.objects.get(slug='christmas')
        form = EventPageForm(instance=event_page)
        panel = SpeakerInlinePanel(instance=event_page, form=form)

        self.assertTrue(panel.is_multipart())

        SpeakerObjectList = ObjectList([
            InlinePanel('speakers', label="Speakers", panels=[
                FieldPanel('first_name', widget=forms.Textarea),
            ]),
        ]).bind_to_model(EventPage)
        SpeakerInlinePanel = SpeakerObjectList.children[0]
        EventPageForm = SpeakerObjectList.get_form_class(EventPage)

        event_page = EventPage.objects.get(slug='christmas')
        form = EventPageForm(instance=event_page)
        panel = SpeakerInlinePanel(instance=event_page, form=form)

        self.assertFalse(panel.is_multipart())
Example #2
0
 def get_edit_handler_class(self):
     if hasattr(self.model, 'edit_handler'):
         edit_handler = self.model.edit_handler
     else:
         panels = extract_panel_definitions_from_model_class(self.model)
         edit_handler = ObjectList(panels)
     return edit_handler.bind_to_model(self.model)
Example #3
0
    def test_render(self):
        """
        Check that the inline panel renders the panels set on the model
        when no 'panels' parameter is passed in the InlinePanel definition
        """
        SpeakerObjectList = ObjectList([InlinePanel('speakers', label="Speakers")]).bind_to_model(EventPage)
        SpeakerInlinePanel = SpeakerObjectList.children[0]
        EventPageForm = SpeakerObjectList.get_form_class(EventPage)

        # SpeakerInlinePanel should instruct the form class to include a 'speakers' formset
        self.assertEqual(['speakers'], list(EventPageForm.formsets.keys()))

        event_page = EventPage.objects.get(slug='christmas')

        form = EventPageForm(instance=event_page)
        panel = SpeakerInlinePanel(instance=event_page, form=form)

        result = panel.render_as_field()

        self.assertIn('<label for="id_speakers-0-first_name">Name:</label>', result)
        self.assertIn('value="Father"', result)
        self.assertIn('<label for="id_speakers-0-last_name">Surname:</label>', result)
        self.assertIn('<label for="id_speakers-0-image">Image:</label>', result)
        self.assertIn('Choose an image', result)

        # rendered panel must also contain hidden fields for id, DELETE and ORDER
        self.assertIn('<input id="id_speakers-0-id" name="speakers-0-id" type="hidden"', result)
        self.assertIn('<input id="id_speakers-0-DELETE" name="speakers-0-DELETE" type="hidden"', result)
        self.assertIn('<input id="id_speakers-0-ORDER" name="speakers-0-ORDER" type="hidden"', result)

        # rendered panel must contain maintenance form for the formset
        self.assertIn('<input id="id_speakers-TOTAL_FORMS" name="speakers-TOTAL_FORMS" type="hidden"', result)

        # render_js_init must provide the JS initializer
        self.assertIn('var panel = InlinePanel({', panel.render_js_init())
Example #4
0
 def get_edit_handler_class(self):
     if hasattr(self.model, 'edit_handler'):
         edit_handler = self.model.edit_handler
     else:
         fields_to_exclude = self.model_admin.get_form_fields_exclude(request=self.request)
         panels = extract_panel_definitions_from_model_class(self.model, exclude=fields_to_exclude)
         edit_handler = ObjectList(panels)
     return edit_handler.bind_to_model(self.model)
Example #5
0
    def test_render_with_panel_overrides(self):
        """
        Check that inline panel renders the panels listed in the InlinePanel definition
        where one is specified
        """
        SpeakerObjectList = ObjectList([
            InlinePanel('speakers', label="Speakers", panels=[
                FieldPanel('first_name', widget=forms.Textarea),
                ImageChooserPanel('image'),
            ]),
        ]).bind_to_model(EventPage)
        SpeakerInlinePanel = SpeakerObjectList.children[0]
        EventPageForm = SpeakerObjectList.get_form_class(EventPage)

        # SpeakerInlinePanel should instruct the form class to include a 'speakers' formset
        self.assertEqual(['speakers'], list(EventPageForm.formsets.keys()))

        event_page = EventPage.objects.get(slug='christmas')

        form = EventPageForm(instance=event_page)
        panel = SpeakerInlinePanel(instance=event_page, form=form)

        result = panel.render_as_field()

        # rendered panel should contain first_name rendered as a text area, but no last_name field
        self.assertIn('<label for="id_speakers-0-first_name">Name:</label>', result)
        self.assertIn('Father</textarea>', result)
        self.assertNotIn('<label for="id_speakers-0-last_name">Surname:</label>', result)

        # test for #338: surname field should not be rendered as a 'stray' label-less field
        self.assertTagInHTML('<input id="id_speakers-0-last_name">', result, count=0, allow_extra_attrs=True)

        self.assertIn('<label for="id_speakers-0-image">Image:</label>', result)
        self.assertIn('Choose an image', result)

        # rendered panel must also contain hidden fields for id, DELETE and ORDER
        self.assertTagInHTML(
            '<input id="id_speakers-0-id" name="speakers-0-id" type="hidden">',
            result, allow_extra_attrs=True
        )
        self.assertTagInHTML(
            '<input id="id_speakers-0-DELETE" name="speakers-0-DELETE" type="hidden">',
            result, allow_extra_attrs=True
        )
        self.assertTagInHTML(
            '<input id="id_speakers-0-ORDER" name="speakers-0-ORDER" type="hidden">',
            result, allow_extra_attrs=True
        )

        # rendered panel must contain maintenance form for the formset
        self.assertTagInHTML(
            '<input id="id_speakers-TOTAL_FORMS" name="speakers-TOTAL_FORMS" type="hidden">',
            result, allow_extra_attrs=True
        )

        # render_js_init must provide the JS initializer
        self.assertIn('var panel = InlinePanel({', panel.render_js_init())
Example #6
0
def get_snippet_edit_handler(model):
    if model not in SNIPPET_EDIT_HANDLERS:
        if hasattr(model, 'edit_handler'):
            # use the edit handler specified on the page class
            edit_handler = model.edit_handler
        else:
            panels = extract_panel_definitions_from_model_class(model)
            edit_handler = ObjectList(panels)

        SNIPPET_EDIT_HANDLERS[model] = edit_handler.bind_to_model(model)

    return SNIPPET_EDIT_HANDLERS[model]
Example #7
0
    def test_autodetect_page_type(self):
        # Model has a foreign key to EventPage, which we want to autodetect
        # instead of specifying the page type in PageChooserPanel
        MyPageObjectList = ObjectList([PageChooserPanel('page')]).bind_to_model(EventPageChooserModel)
        MyPageChooserPanel = MyPageObjectList.children[0]
        PageChooserForm = MyPageObjectList.get_form_class(EventPageChooserModel)
        form = PageChooserForm(instance=self.test_instance)
        page_chooser_panel = MyPageChooserPanel(instance=self.test_instance, form=form)

        result = page_chooser_panel.render_as_field()
        expected_js = 'createPageChooser("{id}", ["{model}"], {parent}, false);'.format(
            id="id_page", model="tests.eventpage", parent=self.events_index_page.id)

        self.assertIn(expected_js, result)
Example #8
0
    def test_override_page_type(self):
        # Model has a foreign key to Page, but we specify EventPage in the PageChooserPanel
        # to restrict the chooser to that page type
        MyPageObjectList = ObjectList([
            PageChooserPanel('page', 'tests.EventPage')
        ]).bind_to_model(EventPageChooserModel)
        MyPageChooserPanel = MyPageObjectList.children[0]
        PageChooserForm = MyPageObjectList.get_form_class(EventPageChooserModel)
        form = PageChooserForm(instance=self.test_instance)
        page_chooser_panel = MyPageChooserPanel(instance=self.test_instance, form=form)

        result = page_chooser_panel.render_as_field()
        expected_js = 'createPageChooser("{id}", ["{model}"], {parent}, false);'.format(
            id="id_page", model="tests.eventpage", parent=self.events_index_page.id)

        self.assertIn(expected_js, result)
 def setUp(self):
     # a custom ObjectList for EventPage
     self.EventPageObjectList = ObjectList([
         FieldPanel('title', widget=forms.Textarea),
         FieldPanel('date_from'),
         FieldPanel('date_to'),
         InlinePanel('speakers', label="Speakers"),
     ], heading='Event details', classname="shiny").bind_to_model(EventPage)
Example #10
0
    def test_render_js_init_with_can_choose_root_true(self):
        # construct an alternative page chooser panel object, with can_choose_root=True

        MyPageObjectList = ObjectList([
            PageChooserPanel('page', can_choose_root=True)
        ]).bind_to_model(PageChooserModel)
        MyPageChooserPanel = MyPageObjectList.children[0]
        PageChooserForm = MyPageObjectList.get_form_class(EventPageChooserModel)

        form = PageChooserForm(instance=self.test_instance)
        page_chooser_panel = MyPageChooserPanel(instance=self.test_instance, form=form)
        result = page_chooser_panel.render_as_field()

        # the canChooseRoot flag on createPageChooser should now be true
        expected_js = 'createPageChooser("{id}", ["{model}"], {parent}, true);'.format(
            id="id_page", model="wagtailcore.page", parent=self.events_index_page.id)
        self.assertIn(expected_js, result)
Example #11
0
class TestObjectList(TestCase):
    def setUp(self):
        # a custom ObjectList for EventPage
        self.EventPageObjectList = ObjectList([
            FieldPanel('title', widget=forms.Textarea),
            FieldPanel('date_from'),
            FieldPanel('date_to'),
            InlinePanel('speakers', label="Speakers"),
        ], heading='Event details', classname="shiny").bind_to_model(EventPage)

    def test_get_form_class(self):
        EventPageForm = self.EventPageObjectList.get_form_class(EventPage)
        form = EventPageForm()

        # form must include the 'speakers' formset required by the speakers InlinePanel
        self.assertIn('speakers', form.formsets)

        # form must respect any overridden widgets
        self.assertEqual(type(form.fields['title'].widget), forms.Textarea)

    def test_render(self):
        EventPageForm = self.EventPageObjectList.get_form_class(EventPage)
        event = EventPage(title='Abergavenny sheepdog trials')
        form = EventPageForm(instance=event)

        object_list = self.EventPageObjectList(
            instance=event,
            form=form
        )

        result = object_list.render()

        # result should contain ObjectList furniture
        self.assertIn('<ul class="objects">', result)

        # result should contain h2 headings (including labels) for children
        self.assertInHTML('<h2><label for="id_date_from">Start date</label></h2>', result)

        # result should include help text for children
        self.assertIn('<div class="object-help help">Not required if event is on a single day</div>', result)

        # result should contain rendered content from descendants
        self.assertIn('Abergavenny sheepdog trials</textarea>', result)

        # this result should not include fields that are not covered by the panel definition
        self.assertNotIn('signup_link', result)
Example #12
0
    def test_no_thousand_separators_in_js(self):
        """
        Test that the USE_THOUSAND_SEPARATOR setting does not screw up the rendering of numbers
        (specifically maxForms=1000) in the JS initializer:
        https://github.com/torchbox/wagtail/pull/2699
        """
        SpeakerObjectList = ObjectList([
            InlinePanel('speakers', label="Speakers", panels=[
                FieldPanel('first_name', widget=forms.Textarea),
                ImageChooserPanel('image'),
            ]),
        ]).bind_to_model(EventPage)
        SpeakerInlinePanel = SpeakerObjectList.children[0]
        EventPageForm = SpeakerObjectList.get_form_class(EventPage)
        event_page = EventPage.objects.get(slug='christmas')
        form = EventPageForm(instance=event_page)
        panel = SpeakerInlinePanel(instance=event_page, form=form)

        self.assertIn('maxForms: 1000', panel.render_js_init())
Example #13
0
    def setUp(self):
        model = PageChooserModel  # a model with a foreign key to Page which we want to render as a page chooser

        # a PageChooserPanel class that works on PageChooserModel's 'page' field
        self.EditHandler = ObjectList([PageChooserPanel('page')]).bind_to_model(PageChooserModel)
        self.MyPageChooserPanel = self.EditHandler.children[0]

        # build a form class containing the fields that MyPageChooserPanel wants
        self.PageChooserForm = self.EditHandler.get_form_class(PageChooserModel)

        # a test instance of PageChooserModel, pointing to the 'christmas' page
        self.christmas_page = Page.objects.get(slug='christmas')
        self.events_index_page = Page.objects.get(slug='events')
        self.test_instance = model.objects.create(page=self.christmas_page)

        self.form = self.PageChooserForm(instance=self.test_instance)
        self.page_chooser_panel = self.MyPageChooserPanel(instance=self.test_instance, form=self.form)
Example #14
0
class ContributorPage(ThemeablePage):
    first_name = models.CharField(max_length=255, blank=True, default="")
    last_name = models.CharField(max_length=255, blank=True, default="")
    nickname = models.CharField(max_length=1024, blank=True, default="")

    email = models.EmailField(blank=True, default="")
    twitter_handle = models.CharField(max_length=16, blank=True, default="")

    short_bio = RichTextField(blank=True, default="")
    long_bio = RichTextField(blank=True, default="")

    headshot = models.ForeignKey(
        'images.AttributedImage',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+'
    )
    featured = models.BooleanField(default=False)

    search_fields = Page.search_fields + [
        index.SearchField('first_name', partial_match=True),
        index.SearchField('last_name', partial_match=True),
        index.SearchField('twitter_handle', partial_match=True),
        index.SearchField('short_bio', partial_match=True),
        index.SearchField('long_bio', partial_match=True),
    ]

    def search_result_text(self):
        if self.short_bio:
            self.search_result_text = self.short_bio
        else:
            self.search_result_text = self.long_bio
        return self.search_result_text

    def save(self, *args, **kwargs):
        if self.twitter_handle and not self.twitter_handle.startswith("@"):
            self.twitter_handle = "@{}".format(self.twitter_handle)
        super(ContributorPage, self).save(*args, **kwargs)

    @property
    def full_name(self):
        return "{} {}".format(self.first_name, self.last_name)

    @property
    def last_comma_first_name(self):
        return "{}, {}".format(self.last_name, self.first_name)

    @property
    def display_twitter_handle(self):
        if self.twitter_handle:
            return self.twitter_handle[1:]
        return self.twitter_handle

    def __str__(self):
        return "{} {} - {}".format(self.first_name, self.last_name, self.email)

    content_panels = Page.content_panels + [
        FieldPanel('first_name'),
        FieldPanel('last_name'),
        FieldPanel('email'),
        FieldPanel('twitter_handle'),
        RichTextFieldPanel('short_bio'),
        RichTextFieldPanel('long_bio'),
        ImageChooserPanel('headshot'),
    ]

    promote_panels = Page.promote_panels + [
        MultiFieldPanel(
            [
                FieldPanel('featured'),
            ],
            heading="Featuring Settings"
        )
    ]

    style_panels = ThemeablePage.style_panels

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(style_panels, heading='Page Style Options'),
        ObjectList(promote_panels, heading='Promote'),
        ObjectList(Page.settings_panels, heading='Settings', classname="settings"),
    ])
Example #15
0
class TestPageChooserPanel(TestCase):
    fixtures = ['test.json']

    def setUp(self):
        model = PageChooserModel  # a model with a foreign key to Page which we want to render as a page chooser

        # a PageChooserPanel class that works on PageChooserModel's 'page' field
        self.EditHandler = ObjectList([PageChooserPanel('page')
                                       ]).bind_to_model(PageChooserModel)
        self.MyPageChooserPanel = self.EditHandler.children[0]

        # build a form class containing the fields that MyPageChooserPanel wants
        self.PageChooserForm = self.EditHandler.get_form_class(
            PageChooserModel)

        # a test instance of PageChooserModel, pointing to the 'christmas' page
        self.christmas_page = Page.objects.get(slug='christmas')
        self.events_index_page = Page.objects.get(slug='events')
        self.test_instance = model.objects.create(page=self.christmas_page)

        self.form = self.PageChooserForm(instance=self.test_instance)
        self.page_chooser_panel = self.MyPageChooserPanel(
            instance=self.test_instance, form=self.form)

    def test_page_chooser_uses_correct_widget(self):
        self.assertEqual(type(self.form.fields['page'].widget),
                         AdminPageChooser)

    def test_render_js_init(self):
        result = self.page_chooser_panel.render_as_field()
        expected_js = 'createPageChooser("{id}", ["{model}"], {parent}, false);'.format(
            id="id_page",
            model="wagtailcore.page",
            parent=self.events_index_page.id)

        self.assertIn(expected_js, result)

    def test_render_js_init_with_can_choose_root_true(self):
        # construct an alternative page chooser panel object, with can_choose_root=True

        MyPageObjectList = ObjectList([
            PageChooserPanel('page', can_choose_root=True)
        ]).bind_to_model(PageChooserModel)
        MyPageChooserPanel = MyPageObjectList.children[0]
        PageChooserForm = MyPageObjectList.get_form_class(
            EventPageChooserModel)

        form = PageChooserForm(instance=self.test_instance)
        page_chooser_panel = MyPageChooserPanel(instance=self.test_instance,
                                                form=form)
        result = page_chooser_panel.render_as_field()

        # the canChooseRoot flag on createPageChooser should now be true
        expected_js = 'createPageChooser("{id}", ["{model}"], {parent}, true);'.format(
            id="id_page",
            model="wagtailcore.page",
            parent=self.events_index_page.id)
        self.assertIn(expected_js, result)

    def test_get_chosen_item(self):
        result = self.page_chooser_panel.get_chosen_item()
        self.assertEqual(result, self.christmas_page)

    def test_render_as_field(self):
        result = self.page_chooser_panel.render_as_field()
        self.assertIn('<p class="help">help text</p>', result)
        self.assertIn('<span class="title">Christmas</span>', result)
        self.assertIn(
            '<a href="/admin/pages/%d/edit/" class="edit-link button button-small button-secondary" target="_blank">'
            'Edit this page</a>' % self.christmas_page.id, result)

    def test_render_as_empty_field(self):
        test_instance = PageChooserModel()
        form = self.PageChooserForm(instance=test_instance)
        page_chooser_panel = self.MyPageChooserPanel(instance=test_instance,
                                                     form=form)
        result = page_chooser_panel.render_as_field()

        self.assertIn('<p class="help">help text</p>', result)
        self.assertIn('<span class="title"></span>', result)
        self.assertIn('Choose a page', result)

    def test_render_error(self):
        form = self.PageChooserForm({'page': ''}, instance=self.test_instance)
        self.assertFalse(form.is_valid())

        page_chooser_panel = self.MyPageChooserPanel(
            instance=self.test_instance, form=form)
        self.assertIn('<span>This field is required.</span>',
                      page_chooser_panel.render_as_field())

    def test_override_page_type(self):
        # Model has a foreign key to Page, but we specify EventPage in the PageChooserPanel
        # to restrict the chooser to that page type
        MyPageObjectList = ObjectList([
            PageChooserPanel('page', 'tests.EventPage')
        ]).bind_to_model(EventPageChooserModel)
        MyPageChooserPanel = MyPageObjectList.children[0]
        PageChooserForm = MyPageObjectList.get_form_class(
            EventPageChooserModel)
        form = PageChooserForm(instance=self.test_instance)
        page_chooser_panel = MyPageChooserPanel(instance=self.test_instance,
                                                form=form)

        result = page_chooser_panel.render_as_field()
        expected_js = 'createPageChooser("{id}", ["{model}"], {parent}, false);'.format(
            id="id_page",
            model="tests.eventpage",
            parent=self.events_index_page.id)

        self.assertIn(expected_js, result)

    def test_autodetect_page_type(self):
        # Model has a foreign key to EventPage, which we want to autodetect
        # instead of specifying the page type in PageChooserPanel
        MyPageObjectList = ObjectList([PageChooserPanel('page')
                                       ]).bind_to_model(EventPageChooserModel)
        MyPageChooserPanel = MyPageObjectList.children[0]
        PageChooserForm = MyPageObjectList.get_form_class(
            EventPageChooserModel)
        form = PageChooserForm(instance=self.test_instance)
        page_chooser_panel = MyPageChooserPanel(instance=self.test_instance,
                                                form=form)

        result = page_chooser_panel.render_as_field()
        expected_js = 'createPageChooser("{id}", ["{model}"], {parent}, false);'.format(
            id="id_page",
            model="tests.eventpage",
            parent=self.events_index_page.id)

        self.assertIn(expected_js, result)

    def test_target_models(self):
        result = PageChooserPanel('barbecue',
                                  'wagtailcore.site').bind_to_model(
                                      PageChooserModel).target_models()
        self.assertEqual(result, [Site])

    def test_target_models_malformed_type(self):
        result = PageChooserPanel('barbecue',
                                  'snowman').bind_to_model(PageChooserModel)
        self.assertRaises(ImproperlyConfigured, result.target_models)

    def test_target_models_nonexistent_type(self):
        result = PageChooserPanel(
            'barbecue', 'snowman.lorry').bind_to_model(PageChooserModel)
        self.assertRaises(ImproperlyConfigured, result.target_models)
Example #16
0
class CFGOVPage(Page):
    authors = ClusterTaggableManager(through=CFGOVAuthoredPages,
                                     blank=True,
                                     verbose_name='Authors',
                                     help_text='A comma separated list of ' +
                                     'authors.',
                                     related_name='authored_pages')
    tags = ClusterTaggableManager(through=CFGOVTaggedPages,
                                  blank=True,
                                  related_name='tagged_pages')
    shared = models.BooleanField(default=False)
    has_unshared_changes = models.BooleanField(default=False)
    language = models.CharField(choices=ref.supported_languagues,
                                default='en',
                                max_length=2)
    social_sharing_image = models.ForeignKey(
        'v1.CFGOVImage',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text=(
            'Optionally select a custom image to appear when users share this '
            'page on social media websites. Recommended size: 1200w x 630h. '
            'Maximum size: 4096w x 4096h.'))

    # This is used solely for subclassing pages we want to make at the CFPB.
    is_creatable = False

    objects = CFGOVPageManager()

    # These fields show up in either the sidebar or the footer of the page
    # depending on the page type.
    sidefoot = StreamField([
        ('call_to_action', molecules.CallToAction()),
        ('related_links', molecules.RelatedLinks()),
        ('related_posts', organisms.RelatedPosts()),
        ('related_metadata', molecules.RelatedMetadata()),
        ('email_signup', organisms.EmailSignUp()),
        ('sidebar_contact', organisms.SidebarContactInfo()),
        ('rss_feed', molecules.RSSFeed()),
        ('social_media', molecules.SocialMedia()),
        ('reusable_text', v1_blocks.ReusableTextChooserBlock(ReusableText)),
    ],
                           blank=True)

    # Panels
    promote_panels = Page.promote_panels + [
        ImageChooserPanel('social_sharing_image'),
    ]

    sidefoot_panels = [
        StreamFieldPanel('sidefoot'),
    ]

    settings_panels = [
        MultiFieldPanel(promote_panels, 'Settings'),
        InlinePanel('categories', label="Categories", max_num=2),
        FieldPanel('tags', 'Tags'),
        FieldPanel('authors', 'Authors'),
        MultiFieldPanel(Page.settings_panels, 'Scheduled Publishing'),
        FieldPanel('language', 'language'),
    ]

    # Tab handler interface guide because it must be repeated for each subclass
    edit_handler = TabbedInterface([
        ObjectList(Page.content_panels, heading='General Content'),
        ObjectList(sidefoot_panels, heading='Sidebar/Footer'),
        ObjectList(settings_panels, heading='Configuration'),
    ])

    def clean(self):
        super(CFGOVPage, self).clean()
        validate_social_sharing_image(self.social_sharing_image)

    def get_authors(self):
        """ Returns a sorted list of authors. Default is alphabetical """
        return self.alphabetize_authors()

    def alphabetize_authors(self):
        """
        Alphabetize authors of this page by last name,
        then first name if needed
        """
        # First sort by first name
        author_names = self.authors.order_by('name')
        # Then sort by last name
        return sorted(author_names, key=lambda x: x.name.split()[-1])

    def generate_view_more_url(self, request):
        """Generate a URL to see more pages like this one.

        This method generates a link to the Activity Log page (which must
        exist and must have a unique site-wide slug of "activity-log") with
        filters set by the tags assigned to this page, like this:

        /activity-log/?topics=foo&topics=bar&topics=baz

        If for some reason a page with slug "activity-log" does not exist,
        this method will raise Page.DoesNotExist.
        """
        activity_log = Page.objects.get(slug='activity-log')
        url = activity_log.get_url(request)

        tags = urlencode([('topics', tag) for tag in self.tags.slugs()])
        if tags:
            url += '?' + tags

        return url

    def related_posts(self, block):
        from v1.models.learn_page import AbstractFilterPage

        def tag_set(related_page):
            return set([tag.pk for tag in related_page.tags.all()])

        def match_all_topic_tags(queryset, page_tags):
            """Return pages that share every one of the current page's tags."""
            current_tag_set = set([tag.pk for tag in page_tags])
            return [
                page for page in queryset
                if current_tag_set.issubset(tag_set(page))
            ]

        related_types = []
        related_items = {}
        if block.value.get('relate_posts'):
            related_types.append('blog')
        if block.value.get('relate_newsroom'):
            related_types.append('newsroom')
        if block.value.get('relate_events'):
            related_types.append('events')
        if not related_types:
            return related_items

        tags = self.tags.all()
        and_filtering = block.value['and_filtering']
        specific_categories = block.value['specific_categories']
        limit = int(block.value['limit'])
        queryset = AbstractFilterPage.objects.live().exclude(
            id=self.id).order_by('-date_published').distinct()

        for parent in related_types:  # blog, newsroom or events
            # Include children of this slug that match at least 1 tag
            children = Page.objects.child_of_q(Page.objects.get(slug=parent))
            filters = children & Q(('tags__in', tags))

            if parent == 'events':
                # Include archived events matches
                archive = Page.objects.get(slug='archive-past-events')
                children = Page.objects.child_of_q(archive)
                filters |= children & Q(('tags__in', tags))

            if specific_categories:
                # Filter by any additional categories specified
                categories = ref.get_appropriate_categories(
                    specific_categories=specific_categories, page_type=parent)
                if categories:
                    filters &= Q(('categories__name__in', categories))

            related_queryset = queryset.filter(filters)

            if and_filtering:
                # By default, we need to match at least one tag
                # If specified in the admin, change this to match ALL tags
                related_queryset = match_all_topic_tags(related_queryset, tags)

            related_items[parent.title()] = related_queryset[:limit]

        # Return items in the dictionary that have non-empty querysets
        return {key: value for key, value in related_items.items() if value}

    def related_metadata_tags(self):
        # Set the tags to correct data format
        tags = {'links': []}
        filter_page = self.get_filter_data()
        for tag in self.specific.tags.all():
            tag_link = {'text': tag.name, 'url': ''}
            if filter_page:
                relative_url = filter_page.relative_url(filter_page.get_site())
                param = '?topics=' + tag.slug
                tag_link['url'] = relative_url + param
            tags['links'].append(tag_link)
        return tags

    def get_filter_data(self):
        for ancestor in self.get_ancestors().reverse().specific():
            if ancestor.specific_class.__name__ in [
                    'BrowseFilterablePage', 'SublandingFilterablePage',
                    'EventArchivePage', 'NewsroomLandingPage'
            ]:
                return ancestor
        return None

    def get_breadcrumbs(self, request):
        ancestors = self.get_ancestors()
        home_page_children = request.site.root_page.get_children()
        for i, ancestor in enumerate(ancestors):
            if ancestor in home_page_children:
                # Add top level parent page and `/process/` url segments
                # where necessary to OAH page breadcrumbs.
                # TODO: Remove this when OAH moves under /consumer-tools
                # and redirects are added after 2018 homebuying campaign.
                if ancestor.slug == 'owning-a-home':
                    breadcrumbs = []
                    for ancestor in ancestors[i:]:
                        ancestor_url = ancestor.relative_url(request.site)
                        if ancestor_url.startswith(
                            ('/owning-a-home/prepare',
                             '/owning-a-home/explore',
                             '/owning-a-home/compare', '/owning-a-home/close',
                             '/owning-a-home/sources')):
                            ancestor_url = ancestor_url.replace(
                                'owning-a-home', 'owning-a-home/process')
                        breadcrumbs.append({
                            'title': ancestor.title,
                            'href': ancestor_url,
                        })
                    return breadcrumbs
                # END TODO
                return [ancestor for ancestor in ancestors[i + 1:]]
        return []

    def get_appropriate_descendants(self, inclusive=True):
        return CFGOVPage.objects.live().descendant_of(self, inclusive)

    def get_appropriate_siblings(self, inclusive=True):
        return CFGOVPage.objects.live().sibling_of(self, inclusive)

    def get_context(self, request, *args, **kwargs):
        context = super(CFGOVPage, self).get_context(request, *args, **kwargs)
        for hook in hooks.get_hooks('cfgovpage_context_handlers'):
            hook(self, request, context, *args, **kwargs)
        return context

    def serve(self, request, *args, **kwargs):
        """
        If request is ajax, then return the ajax request handler response, else
        return the super.
        """
        if request.method == 'POST':
            return self.serve_post(request, *args, **kwargs)

        # Force the page's language on the request
        translation.activate(self.language)
        request.LANGUAGE_CODE = translation.get_language()
        return super(CFGOVPage, self).serve(request, *args, **kwargs)

    def _return_bad_post_response(self, request):
        if request.is_ajax():
            return JsonResponse({'result': 'error'}, status=400)

        return HttpResponseBadRequest(self.url)

    def serve_post(self, request, *args, **kwargs):
        """Handle a POST to a specific form on the page.

        Attempts to retrieve form_id from the POST request, which must be
        formatted like "form-name-index" where the "name" part is the name of a
        StreamField on the page and the "index" part refers to the index of the
        form element in the StreamField.

        If form_id is found, it returns the response from the block method
        retrieval.

        If form_id is not found, it returns an error response.
        """
        form_module = None
        form_id = request.POST.get('form_id', None)

        if form_id:
            form_id_parts = form_id.split('-')

            if len(form_id_parts) == 3:
                streamfield_name = form_id_parts[1]
                streamfield = getattr(self, streamfield_name, None)

                if streamfield is not None:
                    try:
                        streamfield_index = int(form_id_parts[2])
                    except ValueError:
                        streamfield_index = None

                    try:
                        form_module = streamfield[streamfield_index]
                    except IndexError:
                        form_module = None

        if form_module is None:
            return self._return_bad_post_response(request)

        result = form_module.block.get_result(self, request, form_module.value,
                                              True)

        if isinstance(result, HttpResponse):
            return result

        context = self.get_context(request, *args, **kwargs)
        context['form_modules'][streamfield_name].update(
            {streamfield_index: result})

        return TemplateResponse(request,
                                self.get_template(request, *args, **kwargs),
                                context)

    class Meta:
        app_label = 'v1'

    def parent(self):
        parent = self.get_ancestors(inclusive=False).reverse()[0].specific
        return parent

    # To be overriden if page type requires JS files every time
    @property
    def page_js(self):
        return []

    @property
    def streamfield_js(self):
        js = []

        block_cls_names = get_page_blocks(self)
        for block_cls_name in block_cls_names:
            block_cls = import_string(block_cls_name)
            if hasattr(block_cls, 'Media') and hasattr(block_cls.Media, 'js'):
                js.extend(block_cls.Media.js)

        return js

    # Returns the JS files required by this page and its StreamField blocks.
    @property
    def media(self):
        return sorted(set(self.page_js + self.streamfield_js))

    # Returns an image for the page's meta Open Graph tag
    @property
    def meta_image(self):
        return self.social_sharing_image

    @property
    def post_preview_cache_key(self):
        return 'post_preview_{}'.format(self.id)
Example #17
0
class TopicListPage(RoutablePageMixin, ThemeablePage):
    articles_per_page = models.IntegerField(default=20)

    @property
    def topics(self):
        popular_topics = Topic.objects.annotate(
            num_articles=Count('article_links') + Count('articles') + Count('series')).order_by("-num_articles")[:25]
        return sorted(popular_topics, key=lambda x: x.name)

    @route(r'^$', name="topic_list")
    def topics_list(self, request):
        context = {
            "self": self,
        }
        return render(request, "articles/topic_list_page.html", context)

    @route(r'^([\w-]+)/$', name="topic")
    def topic_view(self, request, topic_slug):
        topic = get_object_or_404(Topic, slug=topic_slug)

        articles = topic.item_list

        paginator = Paginator(articles, self.articles_per_page)
        page = request.GET.get('page')

        try:
            articles = paginator.page(page)
        except PageNotAnInteger:
            articles = paginator.page(1)
        except EmptyPage:
            articles = paginator.page(paginator.num_pages)

        context = {
            "self": self,
            "topic": topic,
            "articles": articles,
        }
        return render(request, "articles/topic_page.html", context)

    def get_cached_paths(self):
        yield '/'

        for topic in Topic.objects.all():
            articles = ArticlePage.objects.live().filter(
                models.Q(primary_topic=topic) | models.Q(topic_links__topic=topic)
            ).order_by('-first_published_at').distinct()
            paginator = Paginator(articles, self.articles_per_page)

            topic_url = '/{}/'.format(topic.slug)
            yield topic_url

            for page_number in range(2, paginator.num_pages + 1):
                yield topic_url + '?page=' + str(page_number)

    content_panels = Page.content_panels + [
        FieldPanel('articles_per_page'),
    ]

    style_panels = ThemeablePage.style_panels

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(style_panels, heading='Page Style Options'),
        ObjectList(Page.promote_panels, heading='Promote'),
        ObjectList(Page.settings_panels, heading='Settings', classname="settings"),
    ])
Example #18
0
class Product(models.Model):
    title = models.CharField(unique=True,
                             max_length=255,
                             verbose_name=_('title'))

    description = models.TextField(
        blank=True,
        verbose_name=_('description'),
        help_text=_('For admin/backoffice purposes only.'))

    image = models.ForeignKey('wagtailimages.Image',
                              null=True,
                              blank=True,
                              on_delete=models.SET_NULL,
                              related_name='+')

    product_page = models.OneToOneField(
        ProductPage,
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
    )

    # # TODO or Parental
    categories = TreeManyToManyField(
        'wagtailcommerce.category',
        null=True,
        blank=True,
        #on_delete=models.SET_NULL,
        #related_name='categories'
    )

    sale_price = MoneyField(
        blank=True,
        null=True,
        default=None,
        max_digits=10,
        decimal_places=2,
        help_text=
        _("Base price to compute the customer price. Sometimes called the catalog price."
          ))

    cost_price = MoneyField(blank=True,
                            null=True,
                            default=None,
                            max_digits=10,
                            decimal_places=2,
                            help_text=_("Cost of the product."))

    sku = CharNullableField(
        unique=True,
        null=True,
        blank=True,
        max_length=255,
        verbose_name=_('SKU'),
        help_text=_('Stock Keeping Unit'),
    )

    ean = CharNullableField(
        unique=True,
        null=True,
        blank=True,
        max_length=255,
        verbose_name=_('EAN'),
        help_text=_('European Article Number'),
    )

    general_panels = [
        FieldPanel('title'),
        ImageChooserPanel('image'),
        MultiFieldPanel(
            [
                FieldPanel('sale_price', classname='fn'),
                FieldPanel('cost_price', classname='ln'),
            ],
            heading='Price',
        ),
        MultiFieldPanel(
            [
                FieldPanel('sku'),
                FieldPanel('ean'),
            ],
            heading='Codes',
        ),
        FieldPanel('description'),
    ]

    catalog_panels = [
        PageChooserOrCreatePanel('product_page'),
        FieldPanel('categories'),
        # categories
        # alternative products
        # accessoires/options
        # invoice confirm email (PageChooser)
    ]

    configurator_panels = [
        #FieldPanel('categories')
        # alternative products
        # accessoires/options
        # invoice confirm email (PageChooser)
    ]

    edit_handler = TabbedInterface(
        [
            ObjectList(general_panels, heading='General'),
            ObjectList(catalog_panels, heading='Catalog'),
            # ObjectList(configurator_panels, heading='configurator'),
            # ObjectList([], heading='Inventory'),
            # ObjectList([], heading='Shipping'),
            # ObjectList([], heading='Attributes'),
        ],
        base_form_class=ProductAdminModelForm)

    def __str__(self):
        return "%s" % (self.title)
Example #19
0
# we use this to test that the 'promote' tab is left out of the output when empty
StandardIndex.content_panels = [
    FieldPanel('title', classname="full title"),
    FieldPanel('seo_title'),
    FieldPanel('slug'),
    InlinePanel('advert_placements', label="Adverts"),
]
StandardIndex.promote_panels = []


class StandardChild(Page):
    pass

# Test overriding edit_handler with a custom one
StandardChild.edit_handler = TabbedInterface([
    ObjectList(StandardChild.content_panels, heading='Content'),
    ObjectList(StandardChild.promote_panels, heading='Promote'),
    ObjectList(StandardChild.settings_panels, heading='Settings', classname='settings'),
    ObjectList([], heading='Dinosaurs'),
])

class BusinessIndex(Page):
    """ Can be placed anywhere, can only have Business children """
    subpage_types = ['tests.BusinessChild', 'tests.BusinessSubIndex']


class BusinessSubIndex(Page):
    """ Can be placed under BusinessIndex, and have BusinessChild children """
    subpage_types = ['tests.BusinessChild']
    parent_page_types = ['tests.BusinessIndex']
 def edit_handler_class(self, model):
     """A GenericModelChooserPanel class that works on
     PageChooserModel's 'page' field
     """
     object_list = ObjectList([GenericModelChooserPanel('page')])
     return object_list.bind_to_model(model)
Example #21
0
class TabbedSettings(TestSetting):
    edit_handler = TabbedInterface([
        ObjectList([FieldPanel('title')], heading='First tab'),
        ObjectList([FieldPanel('email')], heading='Second tab'),
    ])
Example #22
0
def get_setting_edit_handler(model):
    panels = extract_panel_definitions_from_model_class(model, ['site'])
    return ObjectList(panels).bind_to_model(model)
Example #23
0
class JournalAdminCreateView(CreateView):
    """
        Journals Create/Add view for wagtail CMS
    """
    panels = [
        MultiFieldPanel([
            FieldPanel('name'),
            FieldPanel('access_length'),
            FieldPanel('organization'),
            FieldPanel('video_course_ids'),
            FieldPanel('price'),
            FieldPanel('currency'),
        ])
    ]
    edit_handler = ObjectList(panels)

    def get_form_kwargs(self):
        """
            Overridden to add request to form kwargs
        """
        kwargs = super(JournalAdminCreateView, self).get_form_kwargs()
        kwargs.update({'request': self.request})
        return kwargs

    def get_edit_handler_class(self):
        """
         Overridden to use custom edit_handle
        """
        return self.edit_handler.bind_to_model(self.model)

    def form_invalid(self, form):
        """
            Overridden to fix for wagtail not displaying non-field related errors.
            Code snippet is taken from wagtail latest release (2.1.1)
        """
        messages.validation_error(self.request, self.get_error_message(), form)
        return self.render_to_response(self.get_context_data())

    def get_form_class(self):
        """
        Returns the form class to use in this view
        """
        return JournalCreateForm

    def create_journal_on_other_services(self, data):
        """
            Using publish_journal command create Journal other services like discovery and e-commerce etc
        """
        stdout, stderr = StringIO(), StringIO()
        try:
            management.call_command('publish_journals',
                                    create=data['name'],
                                    org=data['organization'].name,
                                    access_length=data['access_length'],
                                    price=data['price'],
                                    currency=data['currency'],
                                    publish=False,
                                    stdout=stdout,
                                    stderr=stderr)
        except CommandError as e:
            add_messages(self.request, 'error', [str(e)])

        add_messages(self.request, 'success', stdout.getvalue().split('\n'))
        add_messages(self.request, 'error', stderr.getvalue().split('\n'))

    def post(self, request, *args, **kwargs):
        """
            Overridden to call create journal command if form is valid
        """
        form = self.get_form()
        if form.is_valid():
            response = self.form_valid(form)
            self.create_journal_on_other_services(form.cleaned_data)
            return response
        else:
            return self.form_invalid(form)
Example #24
0
class JournalAdminEditView(EditView):
    """
        Edit view for Journal in wagtail CMS
    """
    panels = [
        MultiFieldPanel([
            FieldPanel('name'),
            FieldPanel('status'),
            FieldPanel('video_course_ids'),
        ])
    ]
    edit_handler = ObjectList(panels)

    def get_edit_handler_class(self):
        """
            Overridden to get custom handler
        """
        return self.edit_handler.bind_to_model(self.model)

    def get_initial(self):
        """
            Overridden to get initial for journal status from discovery
        """
        initials = super(JournalAdminEditView, self).get_initial()
        journal_discovery_results = get_discovery_journal(
            self.request.site.siteconfiguration.discovery_journal_api_client,
            self.instance.uuid)
        if journal_discovery_results:
            initials.update({
                'status':
                True if journal_discovery_results[0]['status'] == 'active' else
                False
            })
        return initials

    def get_form_class(self):
        """
            Returns the form class to use in this view
        """
        return JournalEditForm

    def form_invalid(self, form):
        """
            Overridden to fix for wagtail not displaying non-field related errors.
            Code snippet is taken from wagtail latest release (2.1.1)
        """
        messages.validation_error(self.request, self.get_error_message(), form)
        return self.render_to_response(self.get_context_data())

    def update_journal_on_other_services(self, data):
        """
            Using publish_journal command update Journal other services like discovery and e-commerce etc
        """
        stdout, stderr = StringIO(), StringIO()
        management.call_command('publish_journals',
                                update=self.instance.uuid,
                                publish=data['status'],
                                stdout=stdout,
                                stderr=stderr)

        add_messages(self.request, 'success', stdout.getvalue().split('\n'))
        add_messages(self.request, 'error', stderr.getvalue().split('\n'))

    def post(self, request, *args, **kwargs):
        """
            Overridden to call update journal command if form is valid
        """
        form = self.get_form()
        if form.is_valid():
            response = self.form_valid(form)
            self.update_journal_on_other_services(form.cleaned_data)
            return response
        else:
            return self.form_invalid(form)
Example #25
0
class EventPage(AbstractFilterPage):
    # General content fields
    body = RichTextField('Subheading', blank=True)
    archive_body = RichTextField(blank=True)
    live_body = RichTextField(blank=True)
    future_body = RichTextField(blank=True)
    start_dt = models.DateTimeField("Start", blank=True, null=True)
    end_dt = models.DateTimeField("End", blank=True, null=True)
    future_body = RichTextField(blank=True)
    archive_image = models.ForeignKey('wagtailimages.Image',
                                      null=True,
                                      blank=True,
                                      on_delete=models.SET_NULL,
                                      related_name='+')
    video_transcript = models.ForeignKey('wagtaildocs.Document',
                                         null=True,
                                         blank=True,
                                         on_delete=models.SET_NULL,
                                         related_name='+')
    speech_transcript = models.ForeignKey('wagtaildocs.Document',
                                          null=True,
                                          blank=True,
                                          on_delete=models.SET_NULL,
                                          related_name='+')
    flickr_url = models.URLField("Flickr URL", blank=True)
    youtube_url = models.URLField(
        "Youtube URL",
        blank=True,
        help_text=
        "Format: https://www.youtube.com/embed/video_id. It can be obtained by clicking on Share > Embed on Youtube.",  # noqa: E501
        validators=[
            RegexValidator(regex='^https?:\/\/www\.youtube\.com\/embed\/.*$')
        ])

    live_stream_availability = models.BooleanField("Streaming?",
                                                   default=False,
                                                   blank=True)
    live_stream_url = models.URLField(
        "URL",
        blank=True,
        help_text=
        "Format: https://www.ustream.tv/embed/video_id.  It can be obtained by following the instructions listed here: https://support.ustream.tv/hc/en-us/articles/207851917-How-to-embed-a-stream-or-video-on-your-site",  # noqa: E501
        validators=[
            RegexValidator(regex='^https?:\/\/www\.ustream\.tv\/embed\/.*$')
        ])
    live_stream_date = models.DateTimeField("Go Live Date",
                                            blank=True,
                                            null=True)
    # Venue content fields
    venue_name = models.CharField(max_length=100, blank=True)
    venue_street = models.CharField(max_length=100, blank=True)
    venue_suite = models.CharField(max_length=100, blank=True)
    venue_city = models.CharField(max_length=100, blank=True)
    venue_state = USStateField(blank=True)
    venue_zip = models.IntegerField(blank=True, null=True)
    agenda_items = StreamField([('item', AgendaItemBlock())], blank=True)

    objects = CFGOVPageManager()

    # General content tab
    content_panels = CFGOVPage.content_panels + [
        FieldPanel('body', classname="full"),
        FieldRowPanel([
            FieldPanel('start_dt', classname="col6"),
            FieldPanel('end_dt', classname="col6"),
        ]),
        MultiFieldPanel([
            FieldPanel('archive_body', classname="full"),
            ImageChooserPanel('archive_image'),
            DocumentChooserPanel('video_transcript'),
            DocumentChooserPanel('speech_transcript'),
            FieldPanel('flickr_url'),
            FieldPanel('youtube_url'),
        ],
                        heading='Archive Information'),
        FieldPanel('live_body', classname="full"),
        FieldPanel('future_body', classname="full"),
        MultiFieldPanel([
            FieldPanel('live_stream_availability'),
            FieldPanel('live_stream_url'),
            FieldPanel('live_stream_date'),
        ],
                        heading='Live Stream Information'),
    ]
    # Venue content tab
    venue_panels = [
        FieldPanel('venue_name'),
        MultiFieldPanel([
            FieldPanel('venue_street'),
            FieldPanel('venue_suite'),
            FieldPanel('venue_city'),
            FieldPanel('venue_state'),
            FieldPanel('venue_zip'),
        ],
                        heading='Venue Address'),
    ]
    # Agenda content tab
    agenda_panels = [
        StreamFieldPanel('agenda_items'),
    ]
    # Promotion panels
    promote_panels = [
        MultiFieldPanel(AbstractFilterPage.promote_panels,
                        "Page configuration"),
    ]
    # Tab handler interface
    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='General Content'),
        ObjectList(venue_panels, heading='Venue Information'),
        ObjectList(agenda_panels, heading='Agenda Information'),
        ObjectList(AbstractFilterPage.sidefoot_panels, heading='Sidebar'),
        ObjectList(AbstractFilterPage.settings_panels,
                   heading='Configuration'),
    ])

    template = 'events/event.html'

    def location_image_url(self, scale='2', size='276x155', zoom='12'):
        center = 'Washington, DC'
        if self.venue_city:
            center = self.venue_city
        if self.venue_state:
            center = center + ', ' + self.venue_state
        options = {
            'center': center,
            'scale': scale,
            'size': size,
            'zoom': zoom
        }
        url = 'https://maps.googleapis.com/maps/api/staticmap?'
        return '{url}{options}'.format(url=url, options=urlencode(options))
Example #26
0
class SeriesPage(ThemeablePage, FeatureStyleFields, Promotable, ShareLinksMixin, PageLayoutOptions, VideoDocumentMixin):
    subtitle = RichTextField(blank=True, default="")
    short_description = RichTextField(blank=True, default="")
    body = article_fields.BodyField(blank=True, default="")

    main_image = models.ForeignKey(
        'images.AttributedImage',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+'
    )
    feature_image = models.ForeignKey(
        'images.AttributedImage',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+'
    )
    primary_topic = models.ForeignKey(
        'articles.Topic',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='series'
    )
    project = models.ForeignKey(
        "projects.ProjectPage",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
    )

    search_fields = Page.search_fields + (
        index.SearchField('subtitle', partial_match=True),
        index.SearchField('body', partial_match=True),
        index.SearchField('get_primary_topic_name', partial_match=True),
        index.SearchField('get_topic_names', partial_match=True),
    )

    number_of_related_articles = models.PositiveSmallIntegerField(default=6,
                                                                  verbose_name="Number of Related Articles to Show")

    def get_primary_topic_name(self):
        if self.primary_topic:
            return self.primary_topic.name
        else:
            ""

    def get_topic_names(self):
        return '\n'.join([topic.name if topic else "" for topic in self.topics])

    def get_author_names(self):
        return '\n'.join([author.full_name if author else "" for author in self.authors])

    @property
    def articles(self):
        article_list = []
        for article_link in self.related_article_links.all():
            if article_link.article:
                article_link.article.override_text = article_link.override_text
                article_link.article.override_image = article_link.override_image
                article_list.append(article_link.article)
        return article_list

    @property
    def authors(self):
        author_list = []
        for article_link in self.related_article_links.all():
            if article_link.article:
                if article_link.article:
                    for author_link in article_link.article.author_links.all():
                        if author_link.author:
                            if author_link.author not in author_list:
                                author_list.append(author_link.author)
        author_list.sort(key=attrgetter('last_name'))
        return author_list

    @property
    def topics(self):
        all_topics = []
        if self.primary_topic:
            all_topics.append(self.primary_topic)
        for article_link in self.related_article_links.all():
            if article_link.article:
                all_topics.extend(article_link.article.topics)

        all_topics = list(set(all_topics))
        if all_topics:
            all_topics.sort(key=attrgetter('name'))
        return all_topics

    @property
    def related_series(self):
        related_series_list = []
        if self.project:
            related_series_list = self.project.get_related_series(self)
        return related_series_list

    def related_articles(self, number):
        articles = []
        if self.primary_topic:
            articles = list(ArticlePage.objects.live().filter(primary_topic=self.primary_topic).distinct().order_by(
                '-first_published_at')[:number])

        current_total = len(articles)
        if current_total < number:
            for article in self.articles:
                articles.extend(list(article.related_articles(number)))
                articles = list(set(articles))[:number]
                current_total = len(articles)

                if current_total >= number:
                    return articles

        return articles

    content_panels = Page.content_panels + [
        FieldPanel('subtitle'),
        FieldPanel('short_description'),
        PageChooserPanel('project'),
        ImageChooserPanel('main_image'),
        ImageChooserPanel('feature_image'),
        DocumentChooserPanel('video_document'),
        StreamFieldPanel('body'),
        InlinePanel('related_article_links', label="Articles"),
        SnippetChooserPanel('primary_topic', Topic),
    ]

    promote_panels = Page.promote_panels + [
        MultiFieldPanel(
            [
                FieldPanel('sticky'),
                FieldPanel('sticky_for_type_section'),
                FieldPanel('slippery'),
                FieldPanel('slippery_for_type_section'),
                FieldPanel('editors_pick'),
                FieldPanel('feature_style'),
                FieldPanel('title_size'),
                FieldPanel('fullbleed_feature'),
                FieldPanel('image_overlay_opacity'),
            ],
            heading="Featuring Settings"
        )
    ]

    style_panels = ThemeablePage.style_panels + [
        MultiFieldPanel(
            [
                FieldPanel('include_main_image'),
                FieldPanel('include_main_image_overlay'),
                FieldPanel('full_bleed_image_size'),
                FieldPanel('include_caption_in_footer'),
            ],
            heading="Main Image"
        ),
        MultiFieldPanel(
            [
                FieldPanel('number_of_related_articles'),
            ],
            heading="Sections"
        )
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(style_panels, heading='Page Style Options'),
        ObjectList(promote_panels, heading='Promote'),
        ObjectList(Page.settings_panels, heading='Settings', classname="settings"),
    ])
Example #27
0
class ServicePage(Page):
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    content = RichTextField(
        features=WYSIWYG_FEATURES,
        verbose_name=
        'Write out the steps a resident needs to take to use the service')
    extra_content = StreamField(
        [
            ('content',
             blocks.RichTextBlock(
                 features=WYSIWYG_FEATURES,
                 help_text='Write any additional content describing the service'
             )),
            ('application_block',
             custom_blocks.SnippetChooserBlockWithAPIGoodness(
                 'base.ApplicationBlock')),
        ],
        verbose_name=
        'Add any forms, maps, apps, or content that will help the resident use the service',
    )
    topic = models.ForeignKey(
        'base.Topic',
        on_delete=models.PROTECT,
        related_name='services',
    )

    parent_page_types = ['base.HomePage']
    subpage_types = []
    base_form_class = custom_forms.ServicePageForm

    content_panels = [
        FieldPanel('topic'),
        FieldPanel('title'),
        FieldPanel('content'),
        StreamFieldPanel('extra_content'),
        InlinePanel('contacts', label='Contacts'),
    ]

    api_fields = [
        APIField('content'),
        APIField('extra_content'),
        APIField('topic'),
        APIField('contacts'),
    ]

    es_panels = [
        # TODO: This field comes from Page and django-modeltranslation complains about it
        # FieldPanel('title_es'),
        FieldPanel('content_es'),
    ]

    vi_panels = [
        # TODO: This field comes from Page and django-modeltranslation complains about it
        # FieldPanel('title_es'),
        FieldPanel('content_vi'),
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(es_panels, heading='Spanish', classname='translation-tab'),
        ObjectList(vi_panels,
                   heading='Vietnamese',
                   classname='translation-tab'),
        ObjectList(Page.promote_panels, heading='Promote'),
        # TODO: What should we do with the fields in settings?
        # ObjectList(Page.settings_panels, heading='Settings', classname='settings'),
    ])
Example #28
0
class ArticlePage(ThemeablePage, FeatureStyleFields, Promotable, ShareLinksMixin, PageLayoutOptions, VideoDocumentMixin):
    excerpt = RichTextField(blank=True, default="")
    body = article_fields.BodyField()
    chapters = article_fields.ChapterField(blank=True, null=True)
    table_of_contents_heading = models.TextField(blank=True, default="Table of Contents")
    citations_heading = models.TextField(blank=True, default="Works Cited")
    endnotes_heading = models.TextField(blank=True, default="End Notes")
    endnote_identifier_style = models.CharField(
        max_length=20,
        default="roman-lower",
        choices=(
            ('roman-lower', 'Roman Numerals - Lowercase'),
            ('roman-upper', 'Roman Numerals - Uppercase'),
            ('numbers', 'Numbers')
        )
    )

    main_image = models.ForeignKey(
        'images.AttributedImage',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+'
    )
    feature_image = models.ForeignKey(
        'images.AttributedImage',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+'
    )
    primary_topic = models.ForeignKey(
        'articles.Topic',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='articles'
    )
    category = models.ForeignKey(
        'articles.ArticleCategory',
        related_name='%(class)s',
        on_delete=models.SET_NULL,
        null=True,
        default=1
    )

    include_author_block = models.BooleanField(default=True)

    visualization = models.BooleanField(default=False)
    interview = models.BooleanField(default=False)
    video = models.BooleanField(default=False)
    number_of_related_articles = models.PositiveSmallIntegerField(default=6,
                                                                  verbose_name="Number of Related Articles to Show")
    json_file = article_fields.WagtailFileField(max_length=255, blank=True, null=True, verbose_name='JSON file',
                                                help_text="Only provide if you know your template will be filled with the contents of a JSON data file.")

    project = models.ForeignKey(
        "projects.ProjectPage",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
    )

    search_fields = Page.search_fields + (
        index.SearchField('excerpt', partial_match=True),
        index.SearchField('body', partial_match=True),
        index.SearchField('chapters', partial_match=True),
        index.SearchField('get_primary_topic_name', partial_match=True),
        index.SearchField('get_category_name', partial_match=True),
        index.SearchField('get_topic_names', partial_match=True),
        index.SearchField('get_author_names', partial_match=True),
    )

    def get_primary_topic_name(self):
        if self.primary_topic:
            return self.primary_topic.name
        return ""

    def get_category_name(self):
        if self.category:
            return self.category.name
        return ""

    def get_topic_names(self):
        return '\n'.join([link.topic.name if link.topic else "" for link in self.topic_links.all()])

    def get_author_names(self):
        return '\n'.join(
            [author_link.author.full_name if author_link.author else "" for author_link in self.author_links.all()])

    @property
    def authors(self):
        author_list = []
        for link in self.author_links.all():
            if link.author:
                author_list.append((link.author))
        return author_list

    @property
    def series_articles(self):
        related_series_data = []
        for link in self.series_links.all():
            series_page = link.series
            series_articles = series_page.articles
            series_articles.remove(self)
            related_series_data.append((series_page, series_articles))
        return related_series_data

    @property
    def topics(self):
        primary_topic = self.primary_topic
        all_topics = [link.topic for link in self.topic_links.all()]
        if primary_topic:
            all_topics.append(primary_topic)
        all_topics = list(set(all_topics))
        if len(all_topics) > 0:
            all_topics.sort(key=attrgetter('name'))
        return all_topics

    def related_articles(self, number):
        included = [self.id]
        article_list = []
        if self.primary_topic:
            articles = ArticlePage.objects.live().filter(primary_topic=self.primary_topic).exclude(
                id=self.id).distinct().order_by('-first_published_at')[:number]
            article_list.extend(articles.all())
            included.extend([article.id for article in articles.all()])

        current_total = len(article_list)

        if current_total < number:
            # still don't have enough, so pick using secondary topics
            topics = Topic.objects.filter(article_links__article=self)
            if topics:
                additional_articles = ArticlePage.objects.live().filter(primary_topic__in=topics).exclude(
                    id__in=included).distinct().order_by('-first_published_at')[:number - current_total]
                article_list.extend(additional_articles.all())
                current_total = len(article_list)
                included.extend([article.id for article in additional_articles.all()])

        if current_total < number:
            authors = ContributorPage.objects.live().filter(article_links__article=self)
            if authors:
                additional_articles = ArticlePage.objects.live().filter(author_links__author__in=authors).exclude(
                    id__in=included).distinct().order_by('-first_published_at')[:number - current_total]
                article_list.extend(additional_articles.all())
                current_total = len(article_list)
                included.extend([article.id for article in additional_articles.all()])

        if current_total < number:
            # still don't have enough, so just pick the most recent
            additional_articles = ArticlePage.objects.live().exclude(id__in=included).order_by('-first_published_at')[:number - current_total]
            article_list.extend(additional_articles.all())

        return article_list

    content_panels = Page.content_panels + [
        FieldPanel('excerpt'),
        InlinePanel('author_links', label="Authors"),
        PageChooserPanel('project'),
        ImageChooserPanel('main_image'),
        ImageChooserPanel('feature_image'),
        DocumentChooserPanel('video_document'),
        StreamFieldPanel('body'),
        SnippetChooserPanel('primary_topic', Topic),
        InlinePanel('topic_links', label="Secondary Topics"),
    ]

    advanced_content_panels = [
        FieldPanel('json_file'),
        MultiFieldPanel(
            [
                FieldPanel('table_of_contents_heading'),
                StreamFieldPanel('chapters'),
            ],
            heading="Chapters Section"
        ),
        MultiFieldPanel(
            [
                FieldPanel('endnotes_heading'),
                FieldPanel('endnote_identifier_style'),
                InlinePanel('endnote_links', label="End Notes"),
            ],
            heading="End Notes Section"
        ),
        MultiFieldPanel(
            [
                FieldPanel('citations_heading'),
                InlinePanel('citation_links', label="Citations"),
            ],
            heading="Citations Section"
        ),
    ]

    promote_panels = Page.promote_panels + [
        MultiFieldPanel(
            [
                FieldPanel('sticky'),
                FieldPanel('sticky_for_type_section'),
                FieldPanel('slippery'),
                FieldPanel('slippery_for_type_section'),
                FieldPanel('editors_pick'),
                FieldPanel('feature_style'),
                FieldPanel('title_size'),
                FieldPanel('fullbleed_feature'),
                FieldPanel('image_overlay_opacity'),
            ],
            heading="Featuring Settings"
        ),
    ]

    style_panels = ThemeablePage.style_panels + [

        MultiFieldPanel(
            [
                FieldPanel('include_main_image'),
                FieldPanel('include_main_image_overlay'),
                FieldPanel('full_bleed_image_size'),
                FieldPanel('include_caption_in_footer'),
            ],
            heading="Main Image"
        ),
        MultiFieldPanel(
            [
                InlinePanel('background_image_links', label="Background Images"),
            ],
            heading="Background Images"
        ),
        MultiFieldPanel(
            [
                FieldPanel('include_author_block'),
                FieldPanel('number_of_related_articles')
            ],
            heading="Sections"
        ),
        MultiFieldPanel(
            [
                FieldPanel('interview'),
                FieldPanel('video'),
                FieldPanel('visualization'),
            ],
            heading="Categorization"
        )
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(advanced_content_panels, heading='Advanced Content'),
        ObjectList(style_panels, heading='Page Style Options'),
        ObjectList(promote_panels, heading='Promote'),
        ObjectList(Page.settings_panels, heading='Settings', classname="settings"),
    ])
Example #29
0
def get_invoice_edit_handler(Invoice):
    panels = extract_panel_definitions_from_model_class(
        Invoice, exclude=['invoiceindex'])
    EditHandler = ObjectList(panels).bind_to_model(Invoice)
    return EditHandler
Example #30
0
class TestPageChooserPanel(TestCase):
    fixtures = ['test.json']

    def setUp(self):
        model = PageChooserModel  # a model with a foreign key to Page which we want to render as a page chooser

        # a PageChooserPanel class that works on PageChooserModel's 'page' field
        self.EditHandler = ObjectList([PageChooserPanel('page')]).bind_to_model(PageChooserModel)
        self.MyPageChooserPanel = self.EditHandler.children[0]

        # build a form class containing the fields that MyPageChooserPanel wants
        self.PageChooserForm = self.EditHandler.get_form_class(PageChooserModel)

        # a test instance of PageChooserModel, pointing to the 'christmas' page
        self.christmas_page = Page.objects.get(slug='christmas')
        self.events_index_page = Page.objects.get(slug='events')
        self.test_instance = model.objects.create(page=self.christmas_page)

        self.form = self.PageChooserForm(instance=self.test_instance)
        self.page_chooser_panel = self.MyPageChooserPanel(instance=self.test_instance, form=self.form)

    def test_page_chooser_uses_correct_widget(self):
        self.assertEqual(type(self.form.fields['page'].widget), AdminPageChooser)

    def test_render_js_init(self):
        result = self.page_chooser_panel.render_as_field()
        expected_js = 'createPageChooser("{id}", ["{model}"], {parent}, false);'.format(
            id="id_page", model="wagtailcore.page", parent=self.events_index_page.id)

        self.assertIn(expected_js, result)

    def test_render_js_init_with_can_choose_root_true(self):
        # construct an alternative page chooser panel object, with can_choose_root=True

        MyPageObjectList = ObjectList([
            PageChooserPanel('page', can_choose_root=True)
        ]).bind_to_model(PageChooserModel)
        MyPageChooserPanel = MyPageObjectList.children[0]
        PageChooserForm = MyPageObjectList.get_form_class(EventPageChooserModel)

        form = PageChooserForm(instance=self.test_instance)
        page_chooser_panel = MyPageChooserPanel(instance=self.test_instance, form=form)
        result = page_chooser_panel.render_as_field()

        # the canChooseRoot flag on createPageChooser should now be true
        expected_js = 'createPageChooser("{id}", ["{model}"], {parent}, true);'.format(
            id="id_page", model="wagtailcore.page", parent=self.events_index_page.id)
        self.assertIn(expected_js, result)

    def test_get_chosen_item(self):
        result = self.page_chooser_panel.get_chosen_item()
        self.assertEqual(result, self.christmas_page)

    def test_render_as_field(self):
        result = self.page_chooser_panel.render_as_field()
        self.assertIn('<p class="help">help text</p>', result)
        self.assertIn('<span class="title">Christmas</span>', result)
        self.assertIn(
            '<a href="/admin/pages/%d/edit/" class="edit-link button button-small button-secondary" target="_blank">'
            'Edit this page</a>' % self.christmas_page.id,
            result)

    def test_render_as_empty_field(self):
        test_instance = PageChooserModel()
        form = self.PageChooserForm(instance=test_instance)
        page_chooser_panel = self.MyPageChooserPanel(instance=test_instance, form=form)
        result = page_chooser_panel.render_as_field()

        self.assertIn('<p class="help">help text</p>', result)
        self.assertIn('<span class="title"></span>', result)
        self.assertIn('Choose a page', result)

    def test_render_error(self):
        form = self.PageChooserForm({'page': ''}, instance=self.test_instance)
        self.assertFalse(form.is_valid())

        page_chooser_panel = self.MyPageChooserPanel(instance=self.test_instance, form=form)
        self.assertIn('<span>This field is required.</span>', page_chooser_panel.render_as_field())

    def test_override_page_type(self):
        # Model has a foreign key to Page, but we specify EventPage in the PageChooserPanel
        # to restrict the chooser to that page type
        MyPageObjectList = ObjectList([
            PageChooserPanel('page', 'tests.EventPage')
        ]).bind_to_model(EventPageChooserModel)
        MyPageChooserPanel = MyPageObjectList.children[0]
        PageChooserForm = MyPageObjectList.get_form_class(EventPageChooserModel)
        form = PageChooserForm(instance=self.test_instance)
        page_chooser_panel = MyPageChooserPanel(instance=self.test_instance, form=form)

        result = page_chooser_panel.render_as_field()
        expected_js = 'createPageChooser("{id}", ["{model}"], {parent}, false);'.format(
            id="id_page", model="tests.eventpage", parent=self.events_index_page.id)

        self.assertIn(expected_js, result)

    def test_autodetect_page_type(self):
        # Model has a foreign key to EventPage, which we want to autodetect
        # instead of specifying the page type in PageChooserPanel
        MyPageObjectList = ObjectList([PageChooserPanel('page')]).bind_to_model(EventPageChooserModel)
        MyPageChooserPanel = MyPageObjectList.children[0]
        PageChooserForm = MyPageObjectList.get_form_class(EventPageChooserModel)
        form = PageChooserForm(instance=self.test_instance)
        page_chooser_panel = MyPageChooserPanel(instance=self.test_instance, form=form)

        result = page_chooser_panel.render_as_field()
        expected_js = 'createPageChooser("{id}", ["{model}"], {parent}, false);'.format(
            id="id_page", model="tests.eventpage", parent=self.events_index_page.id)

        self.assertIn(expected_js, result)

    def test_target_models(self):
        result = PageChooserPanel(
            'barbecue',
            'wagtailcore.site'
        ).bind_to_model(PageChooserModel).target_models()
        self.assertEqual(result, [Site])

    def test_target_models_malformed_type(self):
        result = PageChooserPanel(
            'barbecue',
            'snowman'
        ).bind_to_model(PageChooserModel)
        self.assertRaises(ImproperlyConfigured,
                          result.target_models)

    def test_target_models_nonexistent_type(self):
        result = PageChooserPanel(
            'barbecue',
            'snowman.lorry'
        ).bind_to_model(PageChooserModel)
        self.assertRaises(ImproperlyConfigured,
                          result.target_models)

    def test_target_content_type(self):
        with warnings.catch_warnings(record=True) as ws:
            warnings.simplefilter('always')

            result = PageChooserPanel(
                'barbecue',
                'wagtailcore.site'
            ).bind_to_model(PageChooserModel).target_content_type()[0]
            self.assertEqual(result.name, 'site')

            self.assertEqual(len(ws), 1)
            self.assertIs(ws[0].category, RemovedInWagtail17Warning)

    def test_target_content_type_malformed_type(self):
        with warnings.catch_warnings(record=True) as ws:
            warnings.simplefilter('always')

            result = PageChooserPanel(
                'barbecue',
                'snowman'
            ).bind_to_model(PageChooserModel)
            self.assertRaises(ImproperlyConfigured,
                              result.target_content_type)

            self.assertEqual(len(ws), 1)
            self.assertIs(ws[0].category, RemovedInWagtail17Warning)

    def test_target_content_type_nonexistent_type(self):
        with warnings.catch_warnings(record=True) as ws:
            warnings.simplefilter('always')

            result = PageChooserPanel(
                'barbecue',
                'snowman.lorry'
            ).bind_to_model(PageChooserModel)
            self.assertRaises(ImproperlyConfigured,
                              result.target_content_type)
            self.assertEqual(len(ws), 1)
            self.assertIs(ws[0].category, RemovedInWagtail17Warning)
Example #31
0
class RegulationPage(RoutablePageMixin, SecondaryNavigationJSMixin, CFGOVPage):
    """A routable page for serving an eregulations page by Section ID."""

    objects = PageManager()
    parent_page_types = ['regulations3k.RegulationLandingPage']
    subpage_types = []

    template = 'regulations3k/browse-regulation.html'

    header = StreamField([
        ('text_introduction', molecules.TextIntroduction()),
    ],
                         blank=True)

    content = StreamField([
        ('info_unit_group', organisms.InfoUnitGroup()),
        ('full_width_text', RegulationsFullWidthText()),
    ],
                          null=True,
                          blank=True)

    regulation = models.ForeignKey(Part,
                                   blank=True,
                                   null=True,
                                   on_delete=models.PROTECT,
                                   related_name='page')

    content_panels = CFGOVPage.content_panels + [
        StreamFieldPanel('header'),
        StreamFieldPanel('content'),
        FieldPanel('regulation', Part),
    ]

    secondary_nav_exclude_sibling_pages = models.BooleanField(default=False)

    sidefoot_panels = CFGOVPage.sidefoot_panels + [
        FieldPanel('secondary_nav_exclude_sibling_pages'),
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='General Content'),
        ObjectList(sidefoot_panels, heading='Sidebar'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])

    def can_serve_draft_versions(self, request):
        perms = request.user.get_all_permissions()
        if (request.user.is_superuser
                or getattr(request, 'served_by_wagtail_sharing', False)
                or 'regulations3k.change_section' in perms):
            return True
        return False

    def get_versions_query(self, request):
        versions = self.regulation.versions

        if not self.can_serve_draft_versions(request):
            versions = versions.filter(draft=False)

        return versions

    def get_effective_version(self, request, date_str):
        """ Get the requested effective version if the user has permission """
        try:
            effective_version = self.regulation.versions.get(
                effective_date=date_str)
        except EffectiveVersion.DoesNotExist:
            raise Http404

        if (effective_version.draft
                and not self.can_serve_draft_versions(request)):
            raise PermissionDenied

        return effective_version

    def get_section_query(self, effective_version=None):
        """Query set for Sections in this regulation's effective version."""
        if effective_version is None:
            effective_version = self.regulation.effective_version
        return Section.objects.filter(subpart__version=effective_version)

    def get_context(self, request, *args, **kwargs):
        context = super(RegulationPage,
                        self).get_context(request, *args, **kwargs)
        context.update({
            'regulation':
            self.regulation,
            'breadcrumb_items':
            self.get_breadcrumbs(request, *args, **kwargs),
            'search_url':
            (self.get_parent().url + 'search-regulations/results/?regs=' +
             self.regulation.part_number),
            'num_versions':
            self.get_versions_query(request).count(),
        })
        return context

    def get_breadcrumbs(self, request, section=None, **kwargs):
        crumbs = super(RegulationPage, self).get_breadcrumbs(request)

        if section is not None:
            crumbs = crumbs + [
                {
                    'href':
                    self.url + self.reverse_subpage(
                        'index',
                        kwargs={
                            k: v
                            for k, v in kwargs.items() if k == 'date_str'
                        }),
                    'title':
                    str(section.subpart.version.part),
                },
            ]

        return crumbs

    def get_urls_for_version(self, effective_version, section=None):
        base_url = self.get_full_url()
        versions_url = urljoin(base_url, 'versions') + '/'

        if effective_version.live_version:
            # This is the current version
            version_url = base_url
        else:
            # It's a past or future version, URLs have the date str
            date_str = str(effective_version.effective_date)
            version_url = urljoin(base_url, date_str) + '/'
            yield version_url

        if section is not None:
            yield urljoin(version_url, section.label) + '/'
        else:
            sections = self.get_section_query(effective_version)
            yield version_url
            yield versions_url
            for section in sections.all():
                yield urljoin(version_url, section.label) + '/'

    def render_interp(self, context, raw_contents, **kwargs):
        template = get_template('regulations3k/inline_interps.html')

        # Extract the title from the raw regdown
        section_title_match = re.search(r'#+\s?(?P<section_title>.*)\s',
                                        raw_contents)
        if section_title_match is not None:
            context.update({'section_title': section_title_match.group(1)})
            span = section_title_match.span()
            raw_contents = raw_contents[:span[0]] + raw_contents[span[1]:]

        context.update({'contents': regdown(raw_contents)})
        context.update(kwargs)

        return template.render(context)

    @route(r'^(?:(?P<date_str>[0-9]{4}-[0-9]{2}-[0-9]{2})/)?$', name="index")
    def index_route(self, request, date_str=None):
        request.is_preview = getattr(request, 'is_preview', False)

        if date_str is not None:
            effective_version = self.get_effective_version(request, date_str)
            section_query = self.get_section_query(
                effective_version=effective_version)
        else:
            effective_version = self.regulation.effective_version
            section_query = self.get_section_query()

        sections = list(section_query.all())

        context = self.get_context(request)
        context.update({
            'version':
            effective_version,
            'sections':
            sections,
            'get_secondary_nav_items':
            partial(get_secondary_nav_items,
                    sections=sections,
                    date_str=date_str),
        })

        if date_str is not None:
            context['date_str'] = date_str

        return TemplateResponse(request, self.get_template(request), context)

    @route(r'^versions/(?:(?P<section_label>[0-9A-Za-z-]+)/)?$',
           name="versions")
    def versions_page(self, request, section_label=None):
        section_query = self.get_section_query()
        sections = list(section_query.all())
        context = self.get_context(request, sections=sections)

        versions = [{
            'effective_date':
            v.effective_date,
            'date_str':
            str(v.effective_date),
            'sections':
            self.get_section_query(effective_version=v).all(),
            'draft':
            v.draft
        } for v in self.get_versions_query(request).order_by('-effective_date')
                    ]

        context.update({
            'versions':
            versions,
            'section_label':
            section_label,
            'get_secondary_nav_items':
            partial(get_secondary_nav_items, sections=sections),
        })

        return TemplateResponse(request, self.template, context)

    @route(r'^(?:(?P<date_str>[0-9]{4}-[0-9]{2}-[0-9]{2})/)?'
           r'(?P<section_label>[0-9A-Za-z-]+)/$',
           name="section")
    def section_page(self, request, date_str=None, section_label=None):
        """ Render a section of the currently effective regulation """

        if date_str is not None:
            effective_version = self.get_effective_version(request, date_str)
            section_query = self.get_section_query(
                effective_version=effective_version)
        else:
            effective_version = self.regulation.effective_version
            section_query = self.get_section_query()

        next_version = self.get_versions_query(request).filter(
            effective_date__gt=effective_version.effective_date).first()

        kwargs = {}
        if date_str is not None:
            kwargs['date_str'] = date_str

        try:
            section = section_query.get(label=section_label)
        except Section.DoesNotExist:
            return redirect(self.url +
                            self.reverse_subpage("index", kwargs=kwargs))

        sections = list(section_query.all())
        current_index = sections.index(section)
        context = self.get_context(request,
                                   section,
                                   sections=sections,
                                   **kwargs)

        content = regdown(
            section.contents,
            url_resolver=get_url_resolver(self, date_str=date_str),
            contents_resolver=get_contents_resolver(effective_version),
            render_block_reference=partial(self.render_interp, context))

        next_section = get_next_section(sections, current_index)
        previous_section = get_previous_section(sections, current_index)

        context.update({
            'version':
            effective_version,
            'next_version':
            next_version,
            'section':
            section,
            'content':
            content,
            'get_secondary_nav_items':
            partial(get_secondary_nav_items,
                    sections=sections,
                    date_str=date_str),
            'next_section':
            next_section,
            'next_url':
            get_section_url(self, next_section, date_str=date_str),
            'previous_section':
            previous_section,
            'previous_url':
            get_section_url(self, previous_section, date_str=date_str),
        })

        return TemplateResponse(request, self.template, context)
Example #32
0
class RegulationsSearchPage(RoutablePageMixin, CFGOVPage):
    """A page for the custom search interface for regulations."""

    objects = PageManager()

    parent_page_types = ['regulations3k.RegulationLandingPage']
    subpage_types = []
    results = {}
    content_panels = CFGOVPage.content_panels
    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])

    def get_template(self, request):
        template = 'regulations3k/search-regulations.html'
        if 'partial' in request.GET:
            template = 'regulations3k/search-regulations-results.html'
        return template

    @route(r'^results/')
    def regulation_results_page(self, request):
        all_regs = Part.objects.order_by('part_number')
        regs = validate_regs_list(request)
        order = validate_order(request)
        search_query = request.GET.get('q', '').strip()
        payload = {
            'search_query': search_query,
            'results': [],
            'total_results': 0,
            'regs': regs,
            'all_regs': [],
        }
        if not search_query or len(urllib.parse.unquote(search_query)) == 1:
            self.results = payload
            return TemplateResponse(request, self.get_template(request),
                                    self.get_context(request))
        sqs = SearchQuerySet().filter(content=search_query)
        payload.update({
            'all_regs': [{
                'letter_code':
                reg.letter_code,
                'id':
                reg.part_number,
                'num_results':
                sqs.filter(
                    part=reg.part_number).models(SectionParagraph).count(),
                'selected':
                reg.part_number in regs
            } for reg in all_regs]
        })
        payload.update({
            'total_count':
            sum([reg['num_results'] for reg in payload['all_regs']])
        })
        if len(regs) == 1:
            sqs = sqs.filter(part=regs[0])
        elif regs:
            sqs = sqs.filter(part__in=regs)
        if order == 'regulation':
            sqs = sqs.order_by('part', 'section_order')
        sqs = sqs.highlight(pre_tags=['<strong>'],
                            post_tags=['</strong>']).models(SectionParagraph)
        for hit in sqs:
            try:
                snippet = Markup(" ".join(hit.highlighted))
            except TypeError as e:
                logger.warning(
                    "Query string {} produced a TypeError: {}".format(
                        search_query, e))
                continue
            letter_code = LETTER_CODES.get(hit.part)
            hit_payload = {
                'id':
                hit.paragraph_id,
                'part':
                hit.part,
                'reg':
                'Regulation {}'.format(letter_code),
                'label':
                hit.title,
                'snippet':
                snippet,
                'url':
                "{}{}/{}/#{}".format(self.parent().url, hit.part,
                                     hit.section_label, hit.paragraph_id),
            }
            payload['results'].append(hit_payload)
        payload.update({'current_count': sqs.count()})
        self.results = payload
        context = self.get_context(request)
        num_results = validate_num_results(request)
        paginator = Paginator(payload['results'], num_results)
        page_number = validate_page_number(request, paginator)
        paginated_page = paginator.page(page_number)
        context.update({
            'current_count':
            payload['current_count'],
            'total_count':
            payload['total_count'],
            'paginator':
            paginator,
            'current_page':
            page_number,
            'num_results':
            num_results,
            'order':
            order,
            'results':
            paginated_page,
            'show_filters':
            any(reg['selected'] is True for reg in payload['all_regs'])
        })
        return TemplateResponse(request, self.get_template(request), context)
Example #33
0
                                                MultiFieldPanel, ObjectList)
from django.utils.translation import ugettext_lazy as _

menupage_panel = MultiFieldPanel(heading=_("Advanced menu behaviour"),
                                 classname="collapsible collapsed",
                                 children=(
                                     FieldPanel('repeat_in_subnav'),
                                     FieldPanel('repeated_item_text'),
                                 ))
"""
`settings_panels` arrangement, including new menu-related fields from the
MenuPage abstract class.
"""

menupage_settings_panels = (
    MultiFieldPanel(heading=_("Scheduled publishing"),
                    classname="publishing",
                    children=(FieldRowPanel((
                        FieldPanel('go_live_at', classname="col6"),
                        FieldPanel('expire_at', classname="col6"),
                    )), )),
    menupage_panel,
)
"""
The above `settings_panels` arrangement configured as tab, for easier
integration into custom edit_handlers.
"""
menupage_settings_tab = ObjectList(menupage_settings_panels,
                                   heading=_("Settings"),
                                   classname="settings")
Example #34
0
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from django.contrib.auth.decorators import permission_required
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.utils.translation import ugettext as _

from wagtail.wagtailadmin.edit_handlers import ObjectList
from wagtail.wagtailadmin.forms import SearchForm

import models

REDIRECT_EDIT_HANDLER = ObjectList(models.Redirect.content_panels)


@permission_required('wagtailredirects.change_redirect')
def index(request):
    p = request.GET.get("p", 1)
    q = None
    is_searching = False

    if 'q' in request.GET:
        form = SearchForm(request.GET, placeholder_suffix="redirects")
        if form.is_valid():
            q = form.cleaned_data['q']
            is_searching = True

            redirects = models.Redirect.get_for_site(
                site=request.site).prefetch_related('redirect_page').filter(
                    old_path__icontains=q)

    if not is_searching:
Example #35
0
class Category(MP_Node):
    title = models.CharField(unique=True,
                             max_length=255,
                             verbose_name=_('title'))

    active = models.BooleanField(verbose_name=_('active'), default=False)

    search_filter_menu = models.BooleanField(
        verbose_name=_('search filter menu'),
        default=False,
        help_text=
        _('Add Category to search-filters. Direct sub-categories appear as filter options'
          ))

    description = models.TextField(
        blank=True,
        verbose_name=_('description'),
        help_text=_('For admin/backoffice purposes only.'))

    image = models.ForeignKey('wagtailimages.Image',
                              null=True,
                              blank=True,
                              on_delete=models.SET_NULL,
                              related_name='+')

    category_page = models.OneToOneField(
        CategoryPage,
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
    )

    node_order_by = ['title']
    """
    Panels
    """
    general_panels = [
        FieldPanel('title'),
        FieldPanel('active'),
        ImageChooserPanel('image'),
        FieldPanel('description'),
    ]

    catalog_panels = [
        FieldPanel('search_filter_menu'),
        PageChooserOrCreatePanel('category_page'),
    ]

    edit_handler = TabbedInterface([
        ObjectList(general_panels, heading='General'),
        ObjectList(catalog_panels, heading='Catalog'),
        # products
    ])

    base_form_class = MoveNodeForm

    class Meta:
        verbose_name_plural = _('categories')

    def __str__(self):
        return self.title

    def __unicode__(self):
        return self.title

    def create_page(self):
        parent = self.get_first_ancestor_with_category_page()
        category_page = CategoryPage(
            title=self.title,
            image=self.image,
        )

        if parent is not None:
            parent.category_page.add_child(instance=category_page)
        else:
            # If no parent in any of ancerstors, then put it under the (root)index.
            category_index_page = Page.objects.type(CategoryIndexPage).first()
            category_index_page.add_child(instance=category_page)

        self.category_page = category_page

    # TODO Implement
    # Get products total/count in all active children and self
    def get_product_count(self):
        return 25

    def get_first_ancestor_with_category_page(self):
        ancestors = self.get_ancestors()

        for a in reversed(ancestors):
            if a.category_page is not None:
                return a

        return None

    @classmethod
    def get_tree_search_filter_menu(self, ids=[]):
        tree = Category.get_tree_active(ids)
        menu = OrderedDict()

        for category in tree:
            paths = chunk_string_increment(category.path, Category.steplen)

            if category.search_filter_menu:
                menu[category.path] = OrderedDict()
                menu[category.path]['menu_object'] = category
                menu[category.path]['objects'] = []

            if len(paths) > 1:
                ancestor_path = ''.join(paths[-2])

                # Move up, to 'parent' if. And comment about the ancestor path, which is index [-2]
                if ancestor_path in menu:
                    menu[ancestor_path]['objects'].append(category)

        return list(menu.values())

    @classmethod
    def get_tree_active(self, ids=[]):
        """Get tree as DF (depth first) list"""

        if len(ids) > 0:
            categories = Category.objects.select_related(
                'category_page').filter(pk__in=ids, active=True)
        else:
            categories = Category.objects.select_related(
                'category_page').filter(active=True)
        """Ancestors"""
        """First determine per category if all ancsestors active"""
        all_ancestor_paths = []
        # Collect all_ancestor_paths. Merge/combine by category ancestors.
        for c in categories:
            paths = chunk_string_increment(c.path, Category.steplen)
            ancestor_paths = paths[0:-1]
            # union all: of all_ancestors_paths and ancestor_paths of this category
            all_ancestor_paths = list(
                set(all_ancestor_paths) | set(ancestor_paths))

        # Active ancestor_paths, by all active ancestor categories
        # Redundant in case all active categories where queries upfront anyway; len(ids) == 0
        active_ancestor_categories = Category.objects.select_related(
            'category_page').filter(path__in=all_ancestor_paths, active=True)
        active_ancestor_paths = []

        for c in active_ancestor_categories:
            active_ancestor_paths.append(c.path)

        # Determine categories where all ancestors are active
        for idx, category in enumerate(categories):
            paths = chunk_string_increment(category.path, Category.steplen)
            ancestor_paths = paths[0:-1]

            # If any of (Category) ancestor_paths is NOT in active_ancestor_paths (So length is less then)
            if len(ancestor_paths) < len(
                    list(set(ancestor_paths) & set(active_ancestor_paths))):
                del categories[idx]

        # From here all categories have all ancestors active.
        # For categories determine descendants (as DF tree), by active (in) tree-logic
        """Build tree"""
        tree = OrderedDict()

        for category in categories:
            # Potential performace risk of many queries
            # To reduce to raw SQL to determine the active-descendants-tree.
            # And then Category.objects.filter(pk__in=active_descendants_pk)

            # TODO check whether get_tree(category) only returns the sub-tree, under category
            cat_tree = Category.get_tree(category).select_related(
                'category_page')

            # The first cat in cat_tree (DF) list, is category (from container loop),
            # as argument for get_tree(category)
            for cat in cat_tree:
                if cat.active:
                    path = chunk_string_increment(cat.path, Category.steplen)
                    ancestor_paths = path[0:-1]

                    # If active and all ancestor_paths in union/overlap
                    if len(ancestor_paths) == len(
                            list(
                                set(ancestor_paths)
                                & set(active_ancestor_paths))):
                        # Append current (active) Category path to active_ancestor_paths, because we traverse
                        # its children (in next loop) too.
                        if path[-1] not in active_ancestor_paths:
                            active_ancestor_paths.append(path[-1])

                        if cat.path not in tree:
                            tree[cat.path] = cat

        return list(tree.values())
Example #36
0
class BrowsePage(CFGOVPage):
    header = StreamField([
        ('text_introduction', molecules.TextIntroduction()),
        ('featured_content', organisms.FeaturedContent()),
    ],
                         blank=True)

    content = StreamField([
        ('full_width_text', organisms.FullWidthText()),
        ('info_unit_group', organisms.InfoUnitGroup()),
        ('expandable_group', organisms.ExpandableGroup()),
        ('expandable', organisms.Expandable()),
        ('well', organisms.Well()),
        ('video_player', organisms.VideoPlayer()),
        ('snippet_list', organisms.ResourceList()),
        ('table_block',
         organisms.AtomicTableBlock(table_options={'renderer': 'html'})),
        ('feedback', v1_blocks.Feedback()),
        ('raw_html_block', blocks.RawHTMLBlock(label='Raw HTML block')),
        ('conference_registration_form', ConferenceRegistrationForm()),
        ('chart_block', organisms.ChartBlock()),
        ('mortgage_chart_block', organisms.MortgageChartBlock()),
        ('mortgage_map_block', organisms.MortgageMapBlock()),
        ('mortgage_downloads_block', MortgageDataDownloads()),
        ('data_snapshot', organisms.DataSnapshot()),
        ('job_listing_table', JobListingTable()),
        ('bureau_structure', organisms.BureauStructure()),
        ('yes_checklist', YESChecklist()),
    ],
                          blank=True)

    secondary_nav_exclude_sibling_pages = models.BooleanField(default=False)

    # General content tab
    content_panels = CFGOVPage.content_panels + [
        StreamFieldPanel('header'),
        StreamFieldPanel('content'),
    ]

    sidefoot_panels = CFGOVPage.sidefoot_panels + [
        FieldPanel('secondary_nav_exclude_sibling_pages'),
    ]

    # Tab handler interface
    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='General Content'),
        ObjectList(sidefoot_panels, heading='Sidebar'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])

    template = 'browse-basic/index.html'

    objects = PageManager()

    search_fields = CFGOVPage.search_fields + [
        index.SearchField('content'),
        index.SearchField('header')
    ]

    @property
    def page_js(self):
        return (super(BrowsePage, self).page_js + ['secondary-navigation.js'])

    def get_context(self, request, *args, **kwargs):
        context = super(BrowsePage, self).get_context(request, *args, **kwargs)
        context.update({'get_secondary_nav_items': get_secondary_nav_items})
        return context
Example #37
0
 def test_object_list(self):
     object_list = ObjectList(['foo'])
     self.assertTrue(issubclass(object_list, BaseObjectList))