예제 #1
0
class SublandingPage(CFGOVPage):
    header = StreamField([
        ('hero', molecules.Hero()),
    ], blank=True)
    content = StreamField([
        ('text_introduction', molecules.TextIntroduction()),
        ('featured_content', molecules.FeaturedContent()),
        ('image_text_25_75_group', organisms.ImageText2575Group()),
        ('image_text_50_50_group', organisms.ImageText5050Group()),
        ('full_width_text', organisms.FullWidthText()),
        ('half_width_link_blob_group', organisms.HalfWidthLinkBlobGroup()),
        ('third_width_link_blob_group', organisms.ThirdWidthLinkBlobGroup()),
        ('post_preview_snapshot', organisms.PostPreviewSnapshot()),
        ('well', organisms.Well()),
        ('table', organisms.Table(editable=False)),
        ('table_block', organisms.AtomicTableBlock(table_options={'renderer': 'html'})),
        ('contact', organisms.MainContactInfo()),
        ('formfield_with_button', molecules.FormFieldWithButton()),
        ('reg_comment', organisms.RegComment()),
        ('feedback', v1_blocks.Feedback()),
    ], blank=True)
    sidebar_breakout = StreamField([
        ('slug', blocks.CharBlock(icon='title')),
        ('heading', blocks.CharBlock(icon='title')),
        ('paragraph', blocks.RichTextBlock(icon='edit')),
        ('breakout_image', blocks.StructBlock([
            ('image', ImageChooserBlock()),
            ('is_round', blocks.BooleanBlock(required=False, default=True,
                                             label='Round?')),
            ('icon', blocks.CharBlock(help_text='Enter icon class name.')),
            ('heading', blocks.CharBlock(required=False, label='Introduction Heading')),
            ('body', blocks.TextBlock(required=False, label='Introduction Body')),
        ], heading='Breakout Image', icon='image')),
        ('related_posts', organisms.RelatedPosts()),
        ('job_listing_list', JobListingList()),
    ], blank=True)

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

    sidebar_panels = [
        StreamFieldPanel('sidebar_breakout'),
    ] + CFGOVPage.sidefoot_panels

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

    template = 'sublanding-page/index.html'

    objects = PageManager()

    def get_browsefilterable_posts(self, request, limit):
        hostname = request.site.hostname
        filter_pages = [p.specific for p in self.get_appropriate_descendants(hostname)
                        if 'FilterablePage' in p.specific_class.__name__
                        and 'archive' not in p.title.lower()]
        posts_tuple_list = []
        for page in filter_pages:
            base_query = AbstractFilterPage.objects.live_shared(hostname).filter(CFGOVPage.objects.child_of_q(page))
            logger.info('Filtering by parent {}'.format(page))
            form_id = str(page.form_id())
            form = FilterableListForm(hostname=hostname, base_query=base_query)
            for post in form.get_page_set():
                posts_tuple_list.append((form_id, post))
        return sorted(posts_tuple_list, key=lambda p: p[1].date_published, reverse=True)[:limit]
예제 #2
0
class Content(Page):
    body = StreamField([
        ('heading', blocks.CharBlock(classname='full title')),
        ('rich_text', blocks.RichTextBlock()),
        ('raw', blocks.RawHTMLBlock()),
        ('include_content', blocks.CharBlock()),
        ('content_list', blocks.CharBlock()),
    ],
                       null=True,
                       blank=True)
    #    body_simple = StreamField([('rich_text', blocks.RichTextBlock())],null=True, blank=True)
    body_simple = RichTextField(null=True, blank=True)
    date = models.DateField('Content updated date', default=timezone.now)
    background_image = models.ForeignKey('wagtailimages.Image',
                                         null=True,
                                         blank=True,
                                         on_delete=models.SET_NULL,
                                         related_name='+')

    template_filename = models.CharField(max_length=64,
                                         choices=(
                                             ('content.html', 'content.html'),
                                             ('f6-content.html',
                                              'f6-content.html'),
                                             ('f6-vue.html', 'f6-vue.html'),
                                         ),
                                         default='content.html')
    tags = ClusterTaggableManager(through=ContentTag, blank=True)

    def get_template(self, request, *args, **kwargs):
        template_name = request.GET.get('template', self.template_filename)
        #force_template  = request.COOKIES.get('force_template');

        #if force_template == 'f6':
        #   template_name = 'f6-content.html'

        if self.body_simple is not None:
            if len(self.body_simple) > 1:
                template_name = 'f6-content.html'

        return '{}/{}'.format(self.__class__._meta.app_label, template_name)

    promote_panels = Page.promote_panels + [
        FieldPanel('date'),
        FieldPanel('tags'),
    ]

    content_panels = Page.content_panels + [
        StreamFieldPanel('body'),
        FieldPanel('body_simple'),
        ImageChooserPanel('background_image')
    ]

    settings_panels = Page.settings_panels + [FieldPanel('template_filename')]

    search_fields = Page.search_fields + [
        index.SearchField('body'),
        index.SearchField('body_simple'),
        index.FilterField('url_path')
    ]

    def serve(self, request):
        if 'draft' in request.GET:
            return HttpResponseRedirect('/admin/pages/{}/view_draft/'.format(
                self.pk))
        response = super(Content, self).serve(request)
        if 'embed' in request.GET:
            with open(
                    os.path.join(settings.MEDIA_ROOT, 'images',
                                 self.slug + '.html')) as output:
                output.write(response.content)
        return response

    class Meta:
        ordering = ('date', )
예제 #3
0
class FullWidthPage(MenuPage):
    body = StreamField(MyStreamBlock(), blank=True)

    content_panels = Page.content_panels + [
        StreamFieldPanel('body'),
    ]
예제 #4
0
class HTMLPage(CustomPage):
    """General page model containing blocks of HTML content."""
    content = StreamField(HTMLBlock())

    content_panels = Page.content_panels + [StreamFieldPanel("content"), ]
예제 #5
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.",
        validators=[
            RegexValidator(regex=r'^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.youtube.com/embed/video_id.")
    live_stream_date = models.DateTimeField("Go Live Date",
                                            blank=True,
                                            null=True)
    # Venue content fields
    venue_coords = models.CharField(max_length=100, blank=True)
    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_zipcode = models.CharField(max_length=12, blank=True)
    agenda_items = StreamField([('item', AgendaItemBlock())], blank=True)

    objects = CFGOVPageManager()

    search_fields = AbstractFilterPage.search_fields + [
        index.SearchField('body'),
        index.SearchField('archive_body'),
        index.SearchField('live_stream_url'),
        index.SearchField('flickr_url'),
        index.SearchField('youtube_url'),
        index.SearchField('future_body'),
        index.SearchField('agenda_items')
    ]

    # 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_zipcode'),
        ],
                        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'

    @property
    def page_js(self):
        return super(EventPage, self).page_js + ['video-player.js']

    def location_image_url(self, scale='2', size='276x155', zoom='12'):
        if not self.venue_coords:
            self.venue_coords = get_venue_coords(self.venue_city,
                                                 self.venue_state)
        api_url = 'https://api.mapbox.com/styles/v1/mapbox/streets-v11/static'
        static_map_image_url = '{}/{},{}/{}?access_token={}'.format(
            api_url, self.venue_coords, zoom, size,
            settings.MAPBOX_ACCESS_TOKEN)

        return static_map_image_url

    def save(self, *args, **kwargs):
        self.venue_coords = get_venue_coords(self.venue_city, self.venue_state)
        return super(EventPage, self).save(*args, **kwargs)
예제 #6
0
class HomePage(CFGOVPage):
    header = StreamField([
        ('info_unit', molecules.InfoUnit()),
        ('half_width_link_blob', molecules.HalfWidthLinkBlob()),
    ],
                         blank=True)

    # General content tab
    content_panels = CFGOVPage.content_panels + [
        StreamFieldPanel('header'),
        InlinePanel(
            'excluded_updates',
            label='Pages excluded from Latest Updates',
            help_text=('This block automatically displays the six most '
                       'recently published blog posts, press releases, '
                       'speeches, testimonies, events, or op-eds. If you want '
                       'to exclude a page from appearing as a recent update '
                       'in this block, add it as an excluded page.')),
    ]

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

    # Sets page to only be createable at the root
    parent_page_types = ['wagtailcore.Page']

    template = 'index.html'

    objects = PageManager()

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

    def get_category_name(self, category_icon_name):
        cats = dict(ref.limited_categories)
        return cats[str(category_icon_name)]

    def get_context(self, request):
        context = super(HomePage, self).get_context(request)
        context['latest_updates'] = self.get_latest_updates(request)
        return context

    def get_latest_updates(self, request):
        # TODO: There should be a way to express this as part of the query
        # rather than evaluating it in Python.
        excluded_updates_pks = [
            e.excluded_page.pk for e in self.excluded_updates.all()
        ]

        latest_pages = CFGOVPage.objects.in_site(
            request.site).exclude(pk__in=excluded_updates_pks).filter(
                Q(content_type__app_label='v1') &
                (Q(content_type__model='blogpage')
                 | Q(content_type__model='eventpage')
                 | Q(content_type__model='newsroompage')),
                live=True,
            ).distinct().order_by('-first_published_at')[:6]

        return latest_pages
예제 #7
0
 class InvalidStreamModel(models.Model):
     body = StreamField([
         ('heading', blocks.CharBlock()),
         ('rich text', blocks.RichTextBlock()),
     ])
예제 #8
0
class AskPage(PublicBasePage, ContactFields):
    """
    Page type for Ask A Librarian pages.
    """

    ask_widget_name = models.CharField(max_length=100, blank=True)
    reference_resources = RichTextField(blank=True)
    body = StreamField(DefaultBodyFields())
    phone_regex = RegexValidator(regex=PHONE_FORMAT, message=PHONE_ERROR_MSG)
    secondary_phone_number = models.CharField(validators=[phone_regex],
                                              max_length=12,
                                              blank=True)
    schedule_appointment_page = models.ForeignKey(
        'wagtailcore.Page',
        null=True,
        blank=True,
        related_name='+',
        on_delete=models.SET_NULL,
        help_text='Link to a contact form')
    visit_page = models.ForeignKey(
        'wagtailcore.Page',
        null=True,
        blank=True,
        related_name='+',
        on_delete=models.SET_NULL,
        help_text='Link to a location or hours page')

    subpage_types = ['public.StandardPage', 'public.PublicRawHTMLPage']

    content_panels = Page.content_panels + [
        FieldPanel('ask_widget_name'),
        FieldPanel('reference_resources'),
        MultiFieldPanel([
            PageChooserPanel('link_page'),
            FieldPanel('link_external'),
        ],
                        heading='Contact Form'),
        MultiFieldPanel([
            FieldPanel('email'),
            FieldPanel('phone_number'),
            FieldPanel('secondary_phone_number'),
            PageChooserPanel('visit_page'),
            PageChooserPanel('schedule_appointment_page'),
        ],
                        heading='General Contact'),
        StreamFieldPanel('body'),
    ] + PublicBasePage.content_panels

    search_fields = PublicBasePage.search_fields + [
        index.SearchField('ask_widget_name'),
        index.SearchField('reference_resources'),
        index.SearchField('body'),
        index.SearchField('email'),
        index.SearchField('email_label'),
        index.SearchField('phone_number'),
        index.SearchField('body'),
    ]

    @property
    def ask_form_name(self):
        """
        Get the name of the chat widget.

        Returns:
            String, name of the ask widget.
        """
        return self.ask_widget_name

    @property
    def contact_link(self):
        """
        Return an html link for contacting 
        a librarian by email.
        """
        text = '<i class="fa fa-envelope-o fa-2x"></i> Email'
        if self.link_page:
            return '<a href="%s">%s</a>' % (self.link_page.url, text)
        elif self.email:
            return '<a href="mailto:%s">%s</a>' % (self.email, text)
        else:
            return False

    @property
    def has_right_sidebar(self):
        """
        Determine if a right sidebar should
        be displayed on AskPages.

        Returns:
            boolean
        """
        fields = [
            self.contact_link, self.phone_number, self.secondary_phone_number,
            self.schedule_appointment_page, self.visit_page
        ]
        if self.base_has_right_sidebar():
            return True
        else:
            for field in fields:
                if field:
                    return True
        return False

    def get_context(self, request):
        context = super(AskPage, self).get_context(request)
        context['ask_pages'] = AskPage.objects.live()

        return context
예제 #9
0
class Product(UnchainedProductDetailsMixin, TranslatablePage):

    parent_page_types = [ProductListing]

    content = StreamField(
        [
            ('row', SnippetChooserBlock('grid.Row')),
        ] + LIVE_CLEAN_PANEL_BLOCKS,
        default=[],
    )

    content_panels = UnchainedProductDetailsMixin.content_panels + [
        StreamFieldPanel('content'),
    ]

    def get_resource_info(self, *args, **kwargs):
        """Returns the product information"""
        if 'product_slug' in kwargs and 'details_page' in kwargs:
            product_information = get_product_info(kwargs['product_slug'])
            category_names = self.extract_category_info(
                product_information['category_id_list'])
            product_information['category_names'] = category_names
            product_information['attributes'] = self.get_attributes(
                product_information['attributes'])
            related_products = list()
            for related_product in product_information['related_products']:
                related_product_info = get_product_info(
                    related_product['slug'])
                related_product_info['relation_type'] = related_product[
                    'relation_type']
                related_products.append(related_product_info)
            product_information['related_products'] = related_products

            return product_information
        elif 'product_slug' in kwargs:
            return get_product_info(kwargs['product_slug'])
        else:
            return {}

    def product_details(self, request, *args, **kwargs):
        resource_info = self.get_resource_info(product_slug=kwargs['slug'])
        if 'slug' in kwargs and resource_info:
            context = self.get_context(request, *args, **kwargs)
            self.og_title = resource_info.get(
                'name').title() if resource_info.get('name') else None
            self.og_title += ' | Live Clean'
            self.og_description = resource_info.get('description')
            self.og_url = (request.META['HTTP_HOST'] + resource_info.get('url')
                           if resource_info.get('url') else None)
            self.og_type = 'product.item'
            if self.og_title:
                self.title = self.og_title
            if self.og_description:
                self.search_description = self.og_description
            if resource_info.get('images'):
                images_dict = dict(resource_info.get('images')[0])
                if images_dict.get('url'):
                    self.og_image_url = images_dict.get('url')
            context['content'] = WagtailObjectParser.render(
                self,
                request=request,
                is_page=True,
                page_id=self.id,
                product_slug=kwargs['slug'],
                details_page=True)
            return TemplateResponse(
                request,
                'pages/generic/generic_page.html',
                context=context,
            )
        else:
            raise Http404

    def extract_category_info(self, category_id_list):
        """Extracts the category names"""
        categories = get_category_info()
        category_info = list()
        for category_id in category_id_list:
            category = self.get_category(category_id, categories)
            category_info.append(category['name'])
        return category_info

    def get_category(self, category_id, categories):
        """Extracts a category from a list of categories
        if the category's id is present."""
        for category in categories:
            if category['id'] == category_id:
                return category
        return {}

    def get_attributes(self, attributes):
        """Returns attributes with presentation names, if present."""
        product_properties = ProductProperties.objects.all()
        for attribute in attributes:
            attribute_presentation = self.get_attribute_presentation(
                name=attribute['name'], product_properties=product_properties)
            # Check if presentation name is present.
            if attribute_presentation and attribute_presentation.presentation_name:
                attribute[
                    'presentation_name'] = attribute_presentation.presentation_name
            else:
                attribute['presentation_name'] = attribute['name']
            # Check if description presentation is present.
            if attribute_presentation and attribute_presentation.description:
                attribute['description'] = attribute_presentation.description
            # For attributes under attributes
            if attribute['attributes']:
                for attr in attribute['attributes']:
                    attr_presentation = self.get_attribute_presentation(
                        name=attr['name'],
                        product_properties=product_properties)
                    # Check if presentation name is present.
                    if attr_presentation and attr_presentation.presentation_name:
                        attr[
                            'presentation_name'] = attr_presentation.presentation_name
                    else:
                        attribute['presentation_name'] = attribute['name']
                    # Check if description presentation is present.
                    if attr_presentation and attr_presentation.description:
                        attr['description'] = attr_presentation.description
        return attributes

    def get_attribute_presentation(self, name, product_properties):
        attribute = next(
            (attr for attr in product_properties if attr.name == name), None)
        if attribute:
            return attribute
        else:
            return
예제 #10
0
class ShowPage(Page):
    class Meta:
        verbose_name = 'Show'
        description = 'A show microsite'

    api_fields = [
      'description',
      'accent_color',
      'about_content',
      'logo',
    ]

    description = models.CharField(max_length=140, help_text='Describe the show in a tweet (140 characters)')
    accent_color = models.CharField(max_length=7, blank=True, null=True, verbose_name='Accent color')

    about_content = StreamField([
        ('paragraph', blocks.RichTextBlock()),
        ('image', ImageChooserBlock())
    ])

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

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

    social_facebook_url = models.TextField(blank=True, null=True, verbose_name='Facebook URL')
    social_twitter_handle = models.TextField(blank=True, null=True, verbose_name='Twitter handle')
    social_mixcloud_handle = models.TextField(blank=True, null=True, verbose_name='Mixcloud handle')
    social_soundcloud_handle = models.TextField(blank=True, null=True, verbose_name='Soundcloud handle')
    social_youtube_url = models.TextField(blank=True, null=True, verbose_name='YouTube URL')

    feature_interaction = models.BooleanField(
        default=False,
        verbose_name='Interaction',
        help_text='Interaction promotes the URF Text number and @urfstudio in the Player.',
    )

    content_panels = Page.content_panels + [
        FieldPanel('description', classname="full"),
        StreamFieldPanel('about_content')
    ]

    promote_panels = [
        MultiFieldPanel(Page.promote_panels, "Common page configuration"),
        InlinePanel('slots', label='Scheduling Slots'),
        MultiFieldPanel([
            ImageChooserPanel('logo'),
            ImageChooserPanel('cover_image'),
            FieldPanel('accent_color', widget=forms.TextInput(attrs={'type': 'color', 'style': 'height: 50px'}))
        ], 'Branding & design'),
        MultiFieldPanel([
            FieldPanel('feature_interaction'),
        ], heading='Features'),
        MultiFieldPanel([
            FieldPanel('social_facebook_url'),
            FieldPanel('social_twitter_handle'),
            FieldPanel('social_mixcloud_handle'),
            FieldPanel('social_soundcloud_handle'),
            FieldPanel('social_youtube_url'),
        ], heading='Social Pages')
    ]

    parent_page_types = ['shows.ShowIndexPage']
    subpage_types = ['shows.ShowAudioSeriesIndexPage', 'shows.ShowContentPage']

    def has_social(self):
        return self.social_facebook_url\
            or self.social_twitter_handle\
            or self.social_mixcloud_handle\
            or self.social_soundcloud_handle\
            or self.social_youtube_url

    def get_human_time(self):
        slots = self.slots.all()
        slots_human = []

        # TODO: Clean up, inprove english.
        # Fridays 9pm to 10pm
        # Mondays & Wednesdays 10:30am to 11am
        # Weekdays 10:30am to 11am and 10pm to 11pm

        for slot in slots:
            time_display = TimeFormat(slot.from_time).format('g:i a')
            relative = ((slot.day - datetime.now().weekday()) + 7) % 7
            relative_word = ''
            if relative == 0:
                relative_word = 'Today, and every'
            elif relative == 1:
                relative_word = 'Tomorrow, and every'

            slots_human.append('{} {day}{relative_coma} at {time}'.format(
                relative_word,
                day=slot.get_day_display(),
                relative_coma=',' if relative == 1 or relative == 0 else '',
                time=time_display))

        return ', '.join(slots_human)

    def css_style(self, styles):
      return ''.join(['{}:{};'.format(prop, val) for (prop, val) in styles.items()])

    @property
    def safe_accent_color(self):
      if not self.has_accent_color():
        return '#A50027'

      return self.accent_color

    def generate_branding_style(self):
        styles = []

        styles.append('background-color:{}'.format(self.safe_accent_color))

        return ';'.join(styles)

    def generate_branding_style_secondary(self):
      styles = dict();

      if self.has_accent_color:
        accent = Color(self.safe_accent_color)
        accent.luminance = accent.luminance * 0.9 if accent.luminance * 0.9 >= 0 else 0;
        accent.saturation = accent.saturation * 1.1 if accent.saturation * 1.1 <= 1 else 1
        styles['background-color'] = accent.hex

      return self.css_style(styles);

    def has_accent_color(self):
        return self.accent_color is not None and self.accent_color != '#000000'

    def tone_from_accent(self):
        dark_tone = dark_tone_from_accent(self.safe_accent_color[1:])
        return 'dark' if dark_tone else 'light'

    @property
    def tone(self):
      return self.tone_from_accent()

    def name_group(self):
        return self.title and self.title[0] or ''
예제 #11
0
class AbstractArticle(models.Model, RenderInlineMixin):
    is_featured = models.BooleanField(
        verbose_name = _("Is Featured on home page"),
        default=False
    )
    subtitle = models.CharField(
        verbose_name=_('subtitle'),
        max_length=255,
        help_text=_("The subtitle of the page"),
        blank=True
    )
    featured = StreamField([
        ('featured_image', FeaturedImageBlock()),
        ('featured_video', FeaturedVideoBlock()),
        ('featured_audio', FeaturedAudioBlock()),
    ])
    author = models.ForeignKey(
        'articles.AuthorPage',
        verbose_name=_('author'),
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+'
    )
    publication_date = models.DateField(
        verbose_name=_('publication_date'),
        help_text=_("The publication date of the article"),
        default=timezone.now,
        blank=True,
        null=True,
    )
    body = StreamField([
        ('introduction', blocks.TextBlock(icon="italic", rows=3)),
        ('paragraph', blocks.RichTextBlock(icon="pilcrow")),
        # ('markdown_paragraph', MarkdownBlock(icon="code")),
        ('image', ImageChooserBlock(icon="image")),
        ('pullquote', PullQuoteBlock()),
    ])

    class Meta:
        abstract = True
        verbose_name = _("Article")

    def __featured_item(self, block_type='featured_image'):
        for stream_child in self.featured:
            if stream_child.block_type == block_type:
                return stream_child
        return None

    @property
    def featured_image(self):
        return self.__featured_item('featured_image')

    @property
    def featured_audio(self):
        return self.__featured_item('featured_audio')

    @property
    def featured_video(self):
        return self.__featured_item('featured_video')

    @property
    def introduction(self):
        for stream_child in self.body:
            if stream_child.block_type == 'introduction':
                return stream_child.value
        return None
예제 #12
0
class Track(Page):
    page_body = RichTextField(
        blank=True,
        help_text=
        "The main content of the page, above the list of steps and form")
    form_submission_message = models.CharField(
        max_length=255,
        help_text=
        "The text that is shown to the user after they make a form submissiom",
        default="Based on your choices we suggest looking at the following:")
    question = models.CharField(
        max_length=255,
        blank=True,
        help_text="The question for the form on the page (optional)")
    choices = StreamField([
        ('label', blocks.CharBlock(required=True)),
    ],
                          blank=True,
                          null=True)

    has_strict_rules = models.BooleanField(
        default=False,
        help_text=
        "If the rule definitions are strict it will ONLY display results that match the exact answers (instead of the union of the answers)"
    )

    rules = StreamField(
        [('rule',
          blocks.StructBlock([
              ('name', ChoiceRulesBlock()),
              ('pages', blocks.ListBlock(blocks.PageChooserBlock())),
              ('override', blocks.BooleanBlock(default=False, required=False))
          ]))],
        default=[],
        blank=True)

    default_steps = StreamField(
        [('page', blocks.PageChooserBlock())],
        blank=True,
        default=[],
        help_text=
        "The steps to show if someone submits a form with answers that are not covered by the rules"
    )

    content_panels = Page.content_panels + [
        FieldPanel('page_body', classname='full'),
        MultiFieldPanel(
            [
                MultiFieldPanel([
                    FieldPanel('question'),
                    FieldPanel('form_submission_message'),
                    StreamFieldPanel('choices')
                ]),
                FieldPanel('has_strict_rules'),
                StreamFieldPanel('rules'),
                StreamFieldPanel('default_steps'),
            ],
            heading="Options form for the page to help narrow down choices",
            classname="collapsible")
    ]

    template = 'roadmap/track/base.html'

    def steps(self):
        # Get list of all step pages that are descendants of this page
        events = Step.objects.live().descendant_of(self)
        return events

    # Directs people to the walk through or self service routes
    # Walk through path uses the choices model to filter steps to take
    def route(self, request, path_components):
        if len(request.GET):
            return RouteResult(self,
                               kwargs={'template': 'roadmap/track/base.html'})
        else:
            return super(Track, self).route(request, path_components)

    def serve(self, request, template=''):
        if template == '':
            template = self.template
        #Kind of hacky but intercept request if it has 'submit-choice' in the slug
        #Serve the rules for the selected choices
        if len(request.GET):
            #Get selected checkbox values from params in request
            selected_choices = list(request.GET.values())

            #Sort the choices so we have them in the same order as the admin defined rules
            selected_choices.sort()

            pages = []  #list of pages that will be presented to the user
            default_pages = [
            ]  #default pages if there isn't a rule defined for the choices the user selected
            all_selected_choices = ','.join(selected_choices)

            #loop through each admin defined rule to see if we have a defined rule for the selected choices
            if self.has_strict_rules:
                #Find the one rule that matches the selected choices and only suggest those steps
                for rule in self.rules:
                    if rule.value['override'] and re.search(
                            rule.value['name'], all_selected_choices):
                        pages = rule.value['pages']
                        break
                    if rule.value['name'] == all_selected_choices:
                        pages = rule.value['pages']
            else:
                #Union all the pages that match with a rule
                for rule in self.rules:
                    if rule.value['override'] and re.search(
                            rule.value['name'], all_selected_choices):
                        pages = rule.value['pages']
                        break
                    if rule.value['name'] in selected_choices:
                        for page in rule.value['pages']:
                            if page not in pages:
                                pages.append(page)

            for page in self.default_steps:
                #if the user defines default pages in the admin then create a list of pages
                #otherwise the default default_pages list is all the steps in the track
                default_pages.append(Page.objects.get(id=page.value.id))

            if not pages:
                if not default_pages:
                    default_pages = Step.objects.live().descendant_of(self)
                pages = default_pages

            request.path = '/'.join(request.path.split('/')[:3])

            return render(
                request, template, {
                    'steps': list(map((lambda page: page.specific), pages)),
                    'page': self,
                    'selected_choices': ','.join(map(str, selected_choices)),
                    'default_pages': default_pages,
                    'showMessage': True
                })
        #Otherwise just render the track page with the appropriate template
        return render(request, template, {'page': self, 'steps': self.steps()})
예제 #13
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):
        activity_log = CFGOVPage.objects.get(slug='activity-log').specific
        tags = []
        tags = urlencode([('topics', tag) for tag in self.tags.slugs()])
        return (get_protected_url({'request': request}, activity_log) + '?' +
                tags)

    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)
예제 #14
0
class ProgrammePage(Page):

    ### Model Fields ######################################
    short_description = models.CharField(
        blank=True,
        null=True,
        max_length=1000,
        help_text="Short Description to be displayed at the top of the page"
    )

    body = StreamField(
        [
            ('paragraph', RichTextBlock(features=[
                'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'bold', 'italic', 'link', 'ol', 'ul', 'hr'])),
            ('captioned_image', CaptionedImageBlock(label="Image")),
            ('authored_block_quote', AuthoredBlockQuoteBlock(
                label="Block Quote")),
            ('embed', EmbedBlock()),
            ('raw_html', RawHTMLBlock(label="Raw HTML")),
            ('clearfix', ClearfixBlock())
        ],
        null=True
    )

    host = models.CharField(
        blank=True,
        null=True,
        max_length=200,
    )

    genre = models.CharField(
        blank=True,
        null=True,
        max_length=200,
    )

    air_time = models.CharField(
        blank=True,
        null=True,
        max_length=200,
        help_text='Duration of Each Episode',
    )

    tv_rating = models.CharField(
        blank=True,
        null=True,
        max_length=50,
        help_text='Malaysian Standard: U, P13, 18',
    )

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

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

    ### Methods ###########################################
    def episodes(self):
        return [n.episode_ID for n in self.youtube_episode.all()]

    def thumbnail_url(self):
        if(self.thumbnail):
            return get_wagtail_image_url(self.thumbnail)

    def banner_url(self):
        if(self.banner):
            return get_wagtail_image_url(self.banner)

    # Render Stream object to sensible HTML data, then replace
    # <img> tags with the correct src location
    def body_rendered(self):
        text = self.body.render_as_block()
        text = html_replace_img(text)
        return text

    ### CMS and API Exposure ##############################
    # Content panels - What shows up on the CMS dashboard
    content_panels = Page.content_panels + [
        FieldPanel('short_description'),
        StreamFieldPanel('body'),
        MultiFieldPanel(
            [
                FieldPanel('genre'),
                FieldPanel('host'),
                FieldPanel('air_time'),
                FieldPanel('tv_rating')
            ],
            heading="Programme Info",
        ),
        InlinePanel('youtube_episode', label="Youtube Episodes"),
        ImageChooserPanel('banner'),
        ImageChooserPanel('thumbnail'),
    ]

    # API fields - What will be returned from API call
    api_fields = [
        APIField('short_description'),
        APIField('body_rendered'),
        APIField('genre'),
        APIField('host'),
        APIField('air_time'),
        APIField('tv_rating'),
        APIField('episodes'),
        APIField('banner_url'),
        APIField('thumbnail_url'),
    ]

    subpage_types = []
    parent_page_types = ['ProgrammeIndexPage']
예제 #15
0
파일: models.py 프로젝트: pattisdr/cos.io
class CustomPage(Page, index.Indexed):
    footer = ForeignKey('common.Footer',
                        default=DEFAULT_FOOTER_ID,
                        null=True,
                        blank=True,
                        on_delete=SET_NULL,
                        related_name='+')

    content = StreamField([
        ('appeal',
         StructBlock([
             ('icon',
              ChoiceBlock(required=True,
                          choices=[
                              ('none', 'none'),
                              ('flask', 'flask'),
                              ('group', 'group'),
                              ('laptop', 'laptop'),
                              ('sitemap', 'sitemap'),
                              ('user', 'user'),
                              ('book', 'book'),
                              ('download', 'download'),
                          ])),
             ('topic', CharBlock(required=True, max_length=35)),
             ('content', RichTextBlock(required=True)),
         ],
                     classname='appeal',
                     icon='tick',
                     template='common/blocks/appeal.html')),
        ('heading', CharBlock(classname="full title")),
        ('statement', CharBlock()),
        ('paragraph', RichTextBlock()),
        ('imagechooser', ImageChooserBlock()),
        ('column', RowBlock()),
        ('tabs', TabsBlock()),
        ('image', ImageBlock()),
        ('customImage', CustomImageBlock()),
        ('rich_text', RichTextBlock()),
        ('raw_html',
         RawHTMLBlock(
             help_text=
             'With great power comes great responsibility. This HTML is unescaped. Be careful!'
         )),
        ('people_block', PeopleBlock()),
        ('centered_text', CenteredTextBlock()),
        ('hero_block', HeroBlock()),
        ('spotlight_block', SpotlightBlock()),
        ('job_whole_block', JobsWholeBlock()),
        ('embed_block', EmbedBlock()),
        ('whitespaceblock', WhitespaceBlock()),
        ('clear_fixblock', ClearfixBlock()),
        ('code_block', CodeBlock()),
        ('table_block', CustomTableBlock()),
        ('calender_block', GoogleCalendarBlock()),
        ('journal_block', JournalsTabBlock()),
        ('render_file', MfrBlock()),
        ('sponsor_partner_block', SponsorPartnerBlock()),
        ('collapse_block', CollapseBoxBlock()),
        ('button', ButtonBlock()),
    ],
                          null=True,
                          blank=True)

    custom_url = CharField(max_length=256, default='', null=True, blank=True)

    menu_order = IntegerField(
        blank=True,
        default=1,
        help_text=(
            'The order this page should appear in the menu. '
            'The lower the number, the more left the page will appear. '
            'This is required for all pages where "Show in menus" is checked.'
        ))

    search_fields = [
        index.SearchField('content', partial_match=True),
    ]

    content_panels = Page.content_panels + [
        StreamFieldPanel('content'),
        SnippetChooserPanel('footer'),
    ]

    promote_panels = Page.promote_panels + [
        FieldPanel('custom_url'),
        FieldPanel('menu_order'),
        InlinePanel('versioned_redirects', label='URL Versioning'),
    ]

    def serve(self, request):
        return render(
            request, self.template, {
                'page': self,
                'people': Person.objects.all(),
                'jobs': Job.objects.all(),
                'journals': Journal.objects.all(),
                'organizations': Organization.objects.all(),
                'donations': Donation.objects.all(),
                'inkinddonations': InkindDonation.objects.all(),
            })

    @transaction.atomic  # only commit when all descendants are properly updated
    def move(self, target, pos=None):
        """
        Extension to the treebeard 'move' method to ensure that url_path is updated too.
        """
        old_url_path = Page.objects.get(id=self.id).url_path
        super(Page, self).move(target, pos=pos)
        # treebeard's move method doesn't actually update the in-memory instance, so we need to work
        # with a freshly loaded one now
        new_self = Page.objects.get(id=self.id)
        new_url_path = new_self.set_url_path(new_self.get_parent())
        new_self.save()
        new_self._update_descendant_url_paths(old_url_path, new_url_path)
        new_redirect = new_self.versioned_redirects.create()
        redirect_url = ('/' + '/'.join(old_url_path.split('/')[2:]))[:-1]
        new_redirect.old_path = redirect_url
        new_redirect.redirect_page = new_self
        new_redirect.site = new_self.get_site()
        new_redirect.save()
        new_self.redirect_set.add(new_redirect)

        # Log
        logger.info("Page moved: \"%s\" id=%d path=%s", self.title, self.id,
                    new_url_path)

    @transaction.atomic
    # ensure that changes are only committed when we have updated all descendant URL paths, to preserve consistency
    def save(self, *args, **kwargs):
        self.full_clean()

        update_descendant_url_paths = False
        is_new = self.id is None

        if is_new:
            # we are creating a record. If we're doing things properly, this should happen
            # through a treebeard method like add_child, in which case the 'path' field
            # has been set and so we can safely call get_parent
            self.set_url_path(self.get_parent())
        else:
            # Check that we are committing the slug to the database
            # Basically: If update_fields has been specified, and slug is not included, skip this step
            if not ('update_fields' in kwargs
                    and 'slug' not in kwargs['update_fields']):
                # see if the slug has changed from the record in the db, in which case we need to
                # update url_path of self and all descendants
                old_record = Page.objects.get(id=self.id)
                if old_record.slug != self.slug:
                    self.set_url_path(self.get_parent())
                    update_descendant_url_paths = True
                    old_url_path = old_record.url_path
                    new_url_path = self.url_path
                    new_redirect = self.versioned_redirects.create()
                    redirect_url = ('/' +
                                    '/'.join(old_url_path.split('/')[2:]))[:-1]
                    new_redirect.old_path = redirect_url
                    new_redirect.redirect_page = self
                    new_redirect.site = self.get_site()
                    new_redirect.save()
                    self.redirect_set.add(new_redirect)

        for redirect in self.versioned_redirects.all():
            redirect.versioned_redirect_page = self
            redirect.redirect_page = self
            redirect.save()

        result = super(Page, self).save(*args, **kwargs)

        if update_descendant_url_paths:
            self._update_descendant_url_paths(old_url_path, new_url_path)

        # Check if this is a root page of any sites and clear the 'wagtail_site_root_paths' key if so
        if Site.objects.filter(root_page=self).exists():
            cache.delete('wagtail_site_root_paths')

        # Log
        if is_new:
            cls = type(self)
            logger.info(
                "Page created: \"%s\" id=%d content_type=%s.%s path=%s",
                self.title, self.id, cls._meta.app_label, cls.__name__,
                self.url_path)

        return result
예제 #16
0
class ResourcePage(AbstractForm):
    def process_form_submission(self, request_dict):
        return custom_form_submission(self, request_dict)

    def serve(self, request, *args, **kwargs):
        try:
            request_dict = request.POST.dict()

            id = request_dict['id']

            self.process_form_submission(request_dict)

            try:
                cookie = request.COOKIES['ldmw_session']
            except:
                cookie = uid.hex

            resource = get_resource(id, cookie)

            if request_dict['feedback'] == '':
                error = True
            else:
                error = False

            csrf = request.POST.get('csrfmiddlewaretoken')

            resource_result = render_to_string(
                'resources/resource.html', {
                    'page': resource,
                    'like_feedback_submitted': True,
                    'error': error,
                    'csrf_token': csrf
                })

            visited_result = render_to_string(
                'resources/single_visited.html', {
                    'v': resource,
                    'like_feedback_submitted': True
                })

            return JsonResponse({
                'result': resource_result,
                'visited_result': visited_result,
                'id': id,
                'feedback': True
            })

        except:
            request.is_preview = getattr(request, 'is_preview', False)

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

    form_fields = None

    heading = TextField(blank=True,
                        help_text="The title of the resource being linked to")
    logo_background_color = RGBColorField(
        default='#16b28f',
        null=True,
        blank=True,
        help_text="The background colour of brand_logo")
    resource_url = URLField(blank=True,
                            help_text="The url of the resource to link to")
    resource_url_text = TextField(blank=True,
                                  help_text="The text for the url link")
    tagline = RichTextField(
        blank=True, help_text="Bold text that displays on the resource list")
    body = StreamField([
        ('rich_text', blocks.RichTextBlock()),
        ('rawhtml', blocks.RawHTMLBlock()),
        ('heading', blocks.RichTextBlock()),
        ('paragraph', blocks.RichTextBlock()),
        ('column_left', blocks.RichTextBlock()),
        ('column_right', blocks.RichTextBlock()),
        ('image', ImageChooserBlock()),
    ])
    pros = RichTextField(blank=True,
                         help_text="A list of pros for the resource")
    cons = RichTextField(blank=True,
                         help_text="A list of cons for the resource")
    topic_tags = ClusterTaggableManager(
        through=TopicTag,
        blank=True,
        verbose_name='Topic Tags',
        related_name='resource_topic_tags',
        help_text='Topic tags, eg: "sleep", "depression", "stress"')
    issue_tags = ClusterTaggableManager(
        through=IssueTag,
        blank=True,
        verbose_name='Issue Tags',
        related_name='resource_issue_tags',
        help_text='Issue tags, eg: "insomnia", "fatigue", "snoring"')
    reason_tags = ClusterTaggableManager(
        through=ReasonTag,
        blank=True,
        verbose_name='Reason Tags',
        related_name='resource_reason_tags',
        help_text='Reason tags, eg: "loneliness", "relationships"')
    content_tags = ClusterTaggableManager(through=ContentTag,
                                          blank=True,
                                          verbose_name='Content Tags',
                                          related_name='resource_content_tags',
                                          help_text="""
            Content Type tags, eg: "videos", "blogs", "free", "subscription"
        """)
    hidden_tags = ClusterTaggableManager(through=HiddenTag,
                                         blank=True,
                                         verbose_name='Hidden Tags',
                                         related_name='resource_hidden_tags',
                                         help_text='Hidden tags for admin use')
    PRIORITY_CHOICES = (
        (1, '1'),
        (2, '2'),
        (3, '3'),
        (4, '4'),
        (5, '5'),
    )
    priority = IntegerField(choices=PRIORITY_CHOICES,
                            default='5',
                            help_text='Highest priority 1, lowest priority 5')
    background_color = RGBColorField(
        default='#ffffff',
        null=True,
        blank=True,
        help_text="The background colour to use if there is no hero image")
    hero_image = models.ForeignKey('wagtailimages.Image',
                                   null=True,
                                   blank=True,
                                   on_delete=models.SET_NULL,
                                   related_name='+',
                                   help_text="""
            Max file size: 10MB. Choose from: GIF, JPEG, PNG
            (but pick PNG if you have the choice)
        """)
    brand_logo = models.ForeignKey('wagtailimages.Image',
                                   null=True,
                                   blank=True,
                                   on_delete=models.SET_NULL,
                                   related_name='+',
                                   help_text="""
            Max file size: 10MB. Choose from: JPEG, PNG
            (Please upload 155x60 image)
        """)
    brand_text = RichTextField(blank=True)
    text_color = RGBColorField(default='#000000',
                               null=True,
                               blank=True,
                               help_text="""
            The colour of the brand text.
            It should contrast well with the background colour or image
        """)
    latitude = LatitudeField(blank=True,
                             max_length=255,
                             help_text="""
            latitude. This should be a number between -90 and 90
        """)
    longitude = LongitudeField(blank=True,
                               max_length=255,
                               help_text="""
            longitude. This should be a number between -180 and 180
        """)

    search_fields = Page.search_fields + [
        index.SearchField('body'),
        index.SearchField('pros'),
        index.SearchField('cons'),
        index.SearchField('heading'),
        index.SearchField('resource_url'),
        index.RelatedFields('issue_tags', [
            index.SearchField('name'),
        ]),
        index.RelatedFields('content_tags', [
            index.SearchField('name'),
        ]),
        index.RelatedFields('reason_tags', [
            index.SearchField('name'),
        ]),
    ]

    content_panels = Page.content_panels + [
        MultiFieldPanel([
            ImageChooserPanel('hero_image'),
            FieldPanel('background_color'),
            ImageChooserPanel('brand_logo'),
            FieldPanel('brand_text'),
            FieldPanel('text_color')
        ],
                        heading="Branding"),
        InlinePanel('badges', label="Badge"),
        InlinePanel('latlong', label="Latitude and Longitude"),
        FieldPanel('heading', classname="full"),
        FieldRowPanel([
            FieldPanel('logo_background_color', classname="col6"),
        ],
                      classname="full"),
        FieldRowPanel([
            FieldPanel('resource_url', classname="col6"),
            FieldPanel('resource_url_text', classname="col6"),
        ],
                      classname="full"),
        FieldPanel('tagline', classname="full"),
        StreamFieldPanel('body'),
        InlinePanel('buttons', label="Buttons"),
        FieldPanel('pros', classname="full"),
        FieldPanel('cons', classname="full")
    ]

    promote_panels = Page.promote_panels + [
        FieldPanel('topic_tags'),
        FieldPanel('issue_tags'),
        FieldPanel('reason_tags'),
        FieldPanel('content_tags'),
        FieldPanel('hidden_tags'),
        FieldPanel('priority'),
    ]

    def __init__(self, *args, **kwargs):
        self.__class__.objects.prefetch_related('tagged_items__tag')
        super(ResourcePage, self).__init__(*args, **kwargs)
        try:
            self.parent = self.get_parent().slug
        except:
            self.parent = None

    def get_context(self, request):
        context = super(ResourcePage, self).get_context(request)

        if (request.META.get('HTTP_REFERER')
                and request.session.get('results_page')):
            context['back'] = request.session.pop('results_page')

        if 'ldmw_session' in request.COOKIES:
            cookie = request.COOKIES['ldmw_session']
            try:
                context['liked_value'] = Likes.objects\
                    .get(resource_id=self.id, user_hash=cookie)\
                    .like_value
            except:
                context['liked_value'] = 0
        else:
            cookie = ''
            context['liked_value'] = 0

        if 'ldmw_location_latlong' in request.COOKIES:
            try:
                location = request.COOKIES['ldmw_location_latlong']
                [user_lat, user_long] = location.split(",")
                context['is_near'] = any(
                    filter(
                        lambda e: haversine_distance(float(
                            user_lat), float(user_long), float(
                                e.latitude), float(e.longitude)) / 1.6 < 1000,
                        self.latlong.all()))
                # less than 1 mile
            except:
                print("Failed to get location")
                context['is_near'] = False
        else:
            context['is_near'] = False

        Home = apps.get_model('resources', 'home')

        combine_tags = create_tag_combiner(None)
        landing_pages = Home.objects.filter(~Q(slug="home")).live()

        context['landing_pages'] = landing_pages
        context['tags'] = combine_tags(self).specific.tags
        context['number_of_likes'] = Likes.objects\
            .filter(resource_id=self.id, like_value=1)\
            .count()
        context['number_of_dislikes'] = Likes.objects\
            .filter(resource_id=self.id, like_value=-1)\
            .count()
        context['badges'] = ResourcePageBadges.objects\
            .filter(page_id=self.page_ptr_id)
        context['buttons'] = ResourcePageButtons.objects\
            .filter(page_id=self.page_ptr_id)

        return base_context(context, self)

    def get_form_fields(self):
        return iter([])

    def likes(self):
        return Likes.objects\
            .filter(resource_id=self.id, like_value=1)\
            .count()

    def dislikes(self):
        return Likes.objects\
            .filter(resource_id=self.id, like_value=-1)\
            .count()

    class Meta:
        verbose_name = "Resource"
class FaqsPage(Page):
    body = StreamField([
        ('faq_question', blocks.CharBlock(classname="full title")),
        ('faq_answer', blocks.RichTextBlock()),
    ])
예제 #18
0
class HomePage(Page):
    """
    The Home Page. This looks slightly more complicated than it is. You can
    see if you visit your site and edit the homepage that it is split between
    a:
    - Hero area
    - Body area
    - A promotional area
    - Moveable featured site sections
    """

    # Hero section of HomePage
    image = models.ForeignKey('wagtailimages.Image',
                              null=True,
                              blank=True,
                              on_delete=models.SET_NULL,
                              related_name='+',
                              help_text='Homepage image')
    hero_text = models.CharField(
        max_length=255, help_text='Write an introduction for the bakery')
    hero_cta = models.CharField(verbose_name='Hero CTA',
                                max_length=255,
                                help_text='Text to display on Call to Action')
    hero_cta_link = models.ForeignKey(
        'wagtailcore.Page',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        verbose_name='Hero CTA link',
        help_text='Choose a page to link to for the Call to Action')

    # Body section of the HomePage
    body = StreamField(BaseStreamBlock(),
                       verbose_name="Home content block",
                       blank=True)

    # Promo section of the HomePage
    promo_image = models.ForeignKey('wagtailimages.Image',
                                    null=True,
                                    blank=True,
                                    on_delete=models.SET_NULL,
                                    related_name='+',
                                    help_text='Promo image')
    promo_title = models.CharField(
        null=True,
        blank=True,
        max_length=255,
        help_text='Title to display above the promo copy')
    promo_text = RichTextField(null=True,
                               blank=True,
                               help_text='Write some promotional copy')

    # Featured sections on the HomePage
    # You will see on templates/base/home_page.html that these are treated
    # in different ways, and displayed in different areas of the page.
    # Each list their children items that we access via the children function
    # that we define on the individual Page models e.g. BlogIndexPage
    featured_section_1_title = models.CharField(
        null=True,
        blank=True,
        max_length=255,
        help_text='Title to display above the promo copy')
    featured_section_1 = models.ForeignKey(
        'wagtailcore.Page',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text='First featured section for the homepage. Will display up to '
        'three child items.',
        verbose_name='Featured section 1')

    featured_section_2_title = models.CharField(
        null=True,
        blank=True,
        max_length=255,
        help_text='Title to display above the promo copy')
    featured_section_2 = models.ForeignKey(
        'wagtailcore.Page',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text='Second featured section for the homepage. Will display up to '
        'three child items.',
        verbose_name='Featured section 2')

    featured_section_3_title = models.CharField(
        null=True,
        blank=True,
        max_length=255,
        help_text='Title to display above the promo copy')
    featured_section_3 = models.ForeignKey(
        'wagtailcore.Page',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text='Third featured section for the homepage. Will display up to '
        'six child items.',
        verbose_name='Featured section 3')

    content_panels = Page.content_panels + [
        MultiFieldPanel([
            ImageChooserPanel('image'),
            FieldPanel('hero_text', classname="full"),
            MultiFieldPanel([
                FieldPanel('hero_cta'),
                PageChooserPanel('hero_cta_link'),
            ])
        ],
                        heading="Hero section"),
        MultiFieldPanel([
            ImageChooserPanel('promo_image'),
            FieldPanel('promo_title'),
            FieldPanel('promo_text'),
        ],
                        heading="Promo section"),
        StreamFieldPanel('body'),
        MultiFieldPanel([
            MultiFieldPanel([
                FieldPanel('featured_section_1_title'),
                PageChooserPanel('featured_section_1'),
            ]),
            MultiFieldPanel([
                FieldPanel('featured_section_2_title'),
                PageChooserPanel('featured_section_2'),
            ]),
            MultiFieldPanel([
                FieldPanel('featured_section_3_title'),
                PageChooserPanel('featured_section_3'),
            ])
        ],
                        heading="Featured homepage sections",
                        classname="collapsible")
    ]

    def __str__(self):
        return self.title
예제 #19
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.",
        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",
    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'
예제 #20
0
class BlogPage(Page):

    intro = models.CharField(blank=True, max_length=1000)
    content = StreamField([
        ('rich_text', RichTextBlock()),
        ('code_block', CodeBlock()),
        ('google_calendar', GoogleCalendarBlock()),
    ],
                          null=True,
                          blank=True)

    tags = ClusterTaggableManager(through=BlogPageTag, blank=True)
    date = models.DateField(
        _("Post date"),
        default=datetime.datetime.today,
        help_text=_("This date may be displayed on the blog post. It is not "
                    "used to schedule posts to go live at a later date."))

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

    footer = models.ForeignKey('common.Footer',
                               default=DEFAULT_FOOTER_ID,
                               null=True,
                               blank=True,
                               on_delete=SET_NULL,
                               related_name='+')

    blog_authors = models.ManyToManyField(
        Person,
        blank=True,
        through=BlogPagePerson,
        # blank=True, null=True,
    )

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

    blog_categories = models.ManyToManyField(BlogCategory,
                                             through=BlogCategoryBlogPage,
                                             blank=True)

    settings_panels = [
        MultiFieldPanel([
            FieldRowPanel([
                FieldPanel('go_live_at'),
                FieldPanel('expire_at'),
            ],
                          classname="label-above"),
        ],
                        'Scheduled publishing',
                        classname="publishing"),
        FieldPanel('date'),
        # FieldPanel('authors', widget=forms.CheckboxSelectMultiple),
        InlinePanel('authors', label=_("Authors")),
    ]

    promote_panels = Page.promote_panels + [
        FieldPanel('tags'),
    ]

    def get_author(self):
        blog_author_default = Person.objects.filter(user_id=self.owner.id)
        if not blog_author_default:
            return 'Center for Open Science'
        return blog_author_default[0].\
            first_name + " " + blog_author_default[0].last_name

    def get_absolute_url(self):
        return self.url

    def get_blog_index(self):
        # Find closest ancestor which is a blog index
        return self.get_ancestors().type(BlogIndexPage).last()

    def get_context(self, request, *args, **kwargs):
        context = super(BlogPage, self).get_context(request, *args, **kwargs)
        context['blogs'] = self.get_blog_index().blogindexpage.blogs
        context = get_blog_context(context)
        context['COMMENTS_APP'] = COMMENTS_APP
        return context

    class Meta:
        verbose_name = _('Blog page')
        verbose_name_plural = _('Blog pages')

    parent_page_types = ['BlogIndexPage']

    content_panels = Page.content_panels + [
        InlinePanel('categories', label="Categories"),
        ImageChooserPanel('header_image'),
        FieldPanel('intro'),
        StreamFieldPanel('content'),
        SnippetChooserPanel('footer'),
    ]
예제 #21
0
class StreamModel(models.Model):
    body = StreamField([
        ('text', CharBlock()),
        ('rich_text', RichTextBlock()),
        ('image', ImageChooserBlock()),
    ])
예제 #22
0
class HomePage(Page):
    body = StreamField(DemoStreamBlock())
    search_fields = Page.search_fields + (index.SearchField('body'), )

    class Meta:
        verbose_name = "Homepage"
예제 #23
0
class BlogPage(Page, LocalorePromoteFields):
    subtitle = models.CharField(max_length=255, blank=True)

    video_poster_image = models.ForeignKey('localore_admin.LocaloreImage',
                                           verbose_name="poster image",
                                           null=True,
                                           on_delete=models.SET_NULL,
                                           related_name='+')
    video_poster_image_mobile = models.ForeignKey(
        'localore_admin.LocaloreImage',
        verbose_name="poster image (mobile)",
        null=True,
        on_delete=models.SET_NULL,
        related_name='+')
    video_mp4 = models.ForeignKey('wagtaildocs.Document',
                                  null=True,
                                  blank=True,
                                  on_delete=models.SET_NULL,
                                  related_name='+')
    video_webm = models.ForeignKey('wagtaildocs.Document',
                                   null=True,
                                   blank=True,
                                   on_delete=models.SET_NULL,
                                   related_name='+')
    video_ogv = models.ForeignKey('wagtaildocs.Document',
                                  null=True,
                                  blank=True,
                                  on_delete=models.SET_NULL,
                                  related_name='+')
    video_credit_caption = RichTextField(verbose_name="credit caption",
                                         blank=True)

    video_youtube_id = models.CharField(
        verbose_name="YouTube video ID",
        max_length=12,
        blank=True,
        help_text=format_html(
            "The part in bold: "
            "https://www.youtube.com/watch?v=<b>j6IIjLK-8fU</b>"),
    )
    video_is_360 = models.BooleanField(
        "360˚ video",
        default=False,
        help_text="This is a 360-degree video.",
    )

    tile_image = models.ForeignKey(
        'localore_admin.LocaloreImage',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text=("Optional: "
                   "The image to use on the connections index page. "
                   "Will use poster image if not set."))

    date = models.DateField("Post date", default=datetime.date.today)
    is_featured = models.BooleanField(
        "featured",
        default=False,
        help_text=("Makes this connection go to the top of the list "
                   "on the connections index page."),
    )

    intro = RichTextField(blank=True)

    body = StreamField(BlogBodyBlock)

    # search index config
    search_fields = Page.search_fields + [
        index.SearchField('subtitle', partial_match=True),
        index.SearchField('intro', partial_match=True),
        index.SearchField('body', partial_match=True),
    ]

    # admin editor content panels config
    content_panels = Page.content_panels + [
        FieldPanel('subtitle', classname='full'),
        MultiFieldPanel([
            FieldPanel('date'),
            FieldPanel('is_featured'),
        ], "Index page order"),
        MultiFieldPanel([
            ImageChooserPanel('video_poster_image'),
            ImageChooserPanel('video_poster_image_mobile'),
            ImageChooserPanel('tile_image'),
            DocumentChooserPanel('video_mp4'),
            DocumentChooserPanel('video_webm'),
            DocumentChooserPanel('video_ogv'),
            FieldPanel('video_credit_caption'),
        ], "Hero section"),
        MultiFieldPanel([
            FieldPanel('video_youtube_id'),
            FieldPanel('video_is_360'),
        ], "Fullscreen video"),
        FieldPanel('intro', classname='full'),
        StreamFieldPanel('body'),
        InlinePanel('associated_productions', label="Associated Productions"),
        InlinePanel('related_posts', label="Related Connections"),
    ]

    # admin editor metadata panels config
    promote_panels = LocalorePromoteFields.promote_panels

    # parent page/subpage type rules
    parent_page_types = ['blog.BlogIndexPage']
    subpage_types = []

    @property
    def blog_index(self):
        return self.get_ancestors().type(BlogIndexPage).last()

    @property
    def prev_page(self):
        ordered_posts = (BlogPage.objects.live().sibling_of(
            self, inclusive=True).order_by('-is_featured', '-date', '-pk'))
        prev_item = None
        for item in ordered_posts:
            if item == self:
                return prev_item
            prev_item = item

    @property
    def next_page(self):
        ordered_posts = (BlogPage.objects.live().sibling_of(
            self, inclusive=True).order_by('is_featured', 'date', 'pk'))
        prev_item = None
        for item in ordered_posts:
            if item == self:
                return prev_item
            prev_item = item

    @property
    def video_poster_image_file_extension(self):
        return self.video_poster_image.file.url.split('.')[-1]

    @property
    def related_live_posts(self):
        return [
            item.related_blog_page for item in (self.related_posts.all())
            if item.related_blog_page.live
        ]

    @property
    def preview_modes(self):
        return super(BlogPage, self).preview_modes + [
            ('no-video', 'Preview poster image'),
        ]

    def serve_preview(self, request, mode_name):
        if mode_name == 'no-video':
            self.video_mp4 = None

        return super(BlogPage, self).serve_preview(request, mode_name)

    class Meta:
        verbose_name = "Connection"
예제 #24
0
class DefaultRichBlockFieldPage(Page):
    body = StreamField([
        ('rich_text', RichTextBlock()),
    ])

    content_panels = Page.content_panels + [StreamFieldPanel('body')]
예제 #25
0
class AbstractFilterPage(CFGOVPage):
    header = StreamField([
        ('article_subheader', blocks.RichTextBlock(icon='form')),
        ('text_introduction', molecules.TextIntroduction()),
        ('item_introduction', organisms.ItemIntroduction()),
    ],
                         blank=True)
    preview_title = models.CharField(max_length=255, null=True, blank=True)
    preview_subheading = models.CharField(max_length=255,
                                          null=True,
                                          blank=True)
    preview_description = RichTextField(null=True, blank=True)
    secondary_link_url = models.CharField(max_length=500,
                                          null=True,
                                          blank=True)
    secondary_link_text = models.CharField(max_length=255,
                                           null=True,
                                           blank=True)
    preview_image = models.ForeignKey('v1.CFGOVImage',
                                      null=True,
                                      blank=True,
                                      on_delete=models.SET_NULL,
                                      related_name='+')
    date_published = models.DateField(default=date.today)
    date_filed = models.DateField(null=True, blank=True)
    comments_close_by = models.DateField(null=True, blank=True)

    # Configuration tab panels
    settings_panels = [
        MultiFieldPanel(CFGOVPage.promote_panels, 'Settings'),
        InlinePanel('categories', label="Categories", max_num=2),
        FieldPanel('tags', 'Tags'),
        MultiFieldPanel([
            FieldPanel('preview_title', classname="full"),
            FieldPanel('preview_subheading', classname="full"),
            FieldPanel('preview_description', classname="full"),
            FieldPanel('secondary_link_url', classname="full"),
            FieldPanel('secondary_link_text', classname="full"),
            ImageChooserPanel('preview_image'),
        ],
                        heading='Page Preview Fields',
                        classname='collapsible'),
        FieldPanel('authors', 'Authors'),
        MultiFieldPanel([
            FieldPanel('date_published'),
            FieldPanel('date_filed'),
            FieldPanel('comments_close_by'),
        ],
                        'Relevant Dates',
                        classname='collapsible'),
        MultiFieldPanel(Page.settings_panels, 'Scheduled Publishing'),
        FieldPanel('language', 'Language'),
    ]

    # This page class cannot be created.
    is_creatable = False

    objects = CFGOVPageManager()

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

    @classmethod
    def generate_edit_handler(self, content_panel):
        content_panels = [
            StreamFieldPanel('header'),
            content_panel,
        ]
        return TabbedInterface([
            ObjectList(self.content_panels + content_panels,
                       heading='General Content'),
            ObjectList(CFGOVPage.sidefoot_panels, heading='Sidebar'),
            ObjectList(self.settings_panels, heading='Configuration'),
        ])

    # Returns an image for the page's meta Open Graph tag
    @property
    def meta_image(self):
        parent_meta = super(AbstractFilterPage, self).meta_image
        return parent_meta or self.preview_image
예제 #26
0
class PartnerPage(Page):
    """
    Detail view for a specific partner
    """
    introduction = models.TextField(
        help_text='Text to describe the page',
        blank=True)
    image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text='Landscape mode only; horizontal width between 1000px and 3000px.'
    )
    body = StreamField(
        BaseStreamBlock(), verbose_name="Page body", blank=True
    )
    origin = models.ForeignKey(
        Country,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
    )

    # We include related_name='+' to avoid name collisions on relationships.
    # e.g. there are two FooPage models in two different apps,
    # and they both have a FK to bread_type, they'll both try to create a
    # relationship called `foopage_objects` that will throw a valueError on
    # collision.
    partner_type = models.ForeignKey(
        'partners.PartnerType',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+'
    )
    tags = ClusterTaggableManager(through=PartnerPageTag, blank=True)

    content_panels = Page.content_panels + [
        FieldPanel('introduction', classname="full"),
        ImageChooserPanel('image'),
        StreamFieldPanel('body'),
        FieldPanel('origin'),
        FieldPanel('partner_type'),
        FieldPanel('tags'),
    ]

    @property
    def get_tags(self):
        """
        Similar to the authors function above we're returning all the tags that
        are related to the blog post into a list we can access on the template.
        We're additionally adding a URL to access BlogPage objects with that tag
        """
        tags = self.tags.all()
        for tag in tags:
            tag.url = '/'+'/'.join(s.strip('/') for s in [
                self.get_parent().url,
                'tags',
                tag.slug
            ])
        return tags

    @property
    def get_people(self):
        people = self.people_covering.all()
        return people

    search_fields = Page.search_fields + [
        index.SearchField('body'),
        index.SearchField('tags'),
    ]

    parent_page_types = ['PartnersIndexPage']
예제 #27
0
class LocationPage(Page):
    """
    Detail for a specific bakery location.
    """
    introduction = models.TextField(help_text='Text to describe the page',
                                    blank=True)
    image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text=
        'Landscape mode only; horizontal width between 1000px and 3000px.')
    body = StreamField(BaseStreamBlock(), verbose_name="Page body", blank=True)
    address = models.TextField()
    lat_long = models.CharField(
        max_length=36,
        help_text="Comma separated lat/long. (Ex. 64.144367, -21.939182) \
                   Right click Google Maps and select 'What\'s Here'",
        validators=[
            RegexValidator(
                regex='^(\-?\d+(\.\d+)?),\s*(\-?\d+(\.\d+)?)$',
                message=
                'Lat Long must be a comma-separated numeric lat and long',
                code='invalid_lat_long'),
        ])

    # Search index configuration
    search_fields = Page.search_fields + [
        index.SearchField('address'),
        index.SearchField('body'),
    ]

    # Fields to show to the editor in the admin view
    content_panels = [
        FieldPanel('title', classname="full"),
        FieldPanel('introduction', classname="full"),
        ImageChooserPanel('image'),
        StreamFieldPanel('body'),
        FieldPanel('address', classname="full"),
        FieldPanel('lat_long'),
        InlinePanel('hours_of_operation', label="Hours of Operation"),
    ]

    def __str__(self):
        return self.title

    @property
    def operating_hours(self):
        hours = self.hours_of_operation.all()
        return hours

    # Determines if the location is currently open. It is timezone naive
    def is_open(self):
        now = datetime.now()
        current_time = now.time()
        current_day = now.strftime('%a').upper()
        try:
            self.operating_hours.get(day=current_day,
                                     opening_time__lte=current_time,
                                     closing_time__gte=current_time)
            return True
        except LocationOperatingHours.DoesNotExist:
            return False

    # Makes additional context available to the template so that we can access
    # the latitude, longitude and map API key to render the map
    def get_context(self, request):
        context = super(LocationPage, self).get_context(request)
        context['lat'] = self.lat_long.split(",")[0]
        context['long'] = self.lat_long.split(",")[1]
        context['google_map_api_key'] = settings.GOOGLE_MAP_API_KEY
        return context

    # Can only be placed under a LocationsIndexPage object
    parent_page_types = ['LocationsIndexPage']
예제 #28
0
class HomePage(Page):
    featured_content = StreamField([
        ("featured_section", FeaturedSectionBlock()),
    ],
                                   blank=True,
                                   null=True)

    # TODO: Carousel is temporary and being phased out
    carousel_0 = models.ForeignKey("wagtailcore.Page",
                                   null=True,
                                   blank=True,
                                   on_delete=models.SET_NULL,
                                   related_name="+")
    carousel_1 = models.ForeignKey("wagtailcore.Page",
                                   null=True,
                                   blank=True,
                                   on_delete=models.SET_NULL,
                                   related_name="+")
    carousel_2 = models.ForeignKey("wagtailcore.Page",
                                   null=True,
                                   blank=True,
                                   on_delete=models.SET_NULL,
                                   related_name="+")
    carousel_3 = models.ForeignKey("wagtailcore.Page",
                                   null=True,
                                   blank=True,
                                   on_delete=models.SET_NULL,
                                   related_name="+")
    carousel_4 = models.ForeignKey("wagtailcore.Page",
                                   null=True,
                                   blank=True,
                                   on_delete=models.SET_NULL,
                                   related_name="+")
    carousel_5 = models.ForeignKey("wagtailcore.Page",
                                   null=True,
                                   blank=True,
                                   on_delete=models.SET_NULL,
                                   related_name="+")
    carousel_6 = models.ForeignKey("wagtailcore.Page",
                                   null=True,
                                   blank=True,
                                   on_delete=models.SET_NULL,
                                   related_name="+")
    carousel_7 = models.ForeignKey("wagtailcore.Page",
                                   null=True,
                                   blank=True,
                                   on_delete=models.SET_NULL,
                                   related_name="+")
    carousel_8 = models.ForeignKey("wagtailcore.Page",
                                   null=True,
                                   blank=True,
                                   on_delete=models.SET_NULL,
                                   related_name="+")
    carousel_9 = models.ForeignKey("wagtailcore.Page",
                                   null=True,
                                   blank=True,
                                   on_delete=models.SET_NULL,
                                   related_name="+")
    in_focus_title = models.TextField(blank=True,
                                      null=True,
                                      verbose_name="Title")
    in_focus_link = models.TextField(blank=True,
                                     null=True,
                                     verbose_name="Link")
    in_focus_link_text = models.TextField(blank=True,
                                          null=True,
                                          verbose_name="Link Text")
    in_focus_page1 = models.ForeignKey("wagtailcore.Page",
                                       null=True,
                                       blank=True,
                                       on_delete=models.PROTECT,
                                       related_name="+",
                                       verbose_name="Page one")
    in_focus_page2 = models.ForeignKey("wagtailcore.Page",
                                       null=True,
                                       blank=True,
                                       on_delete=models.PROTECT,
                                       related_name="+",
                                       verbose_name="Page two")

    talking_album = models.ForeignKey("album.Album",
                                      null=True,
                                      blank=True,
                                      related_name='+',
                                      on_delete=models.PROTECT)
    photo_album = models.ForeignKey("album.Album",
                                    null=True,
                                    blank=True,
                                    related_name='+',
                                    on_delete=models.PROTECT)
    video = models.ForeignKey("article.Article",
                              null=True,
                              blank=True,
                              on_delete=models.PROTECT)

    language = models.CharField(max_length=7, choices=settings.LANGUAGES)

    content_panels = Page.content_panels + [
        MultiFieldPanel([StreamFieldPanel('featured_content')],
                        heading="Featured Content",
                        classname="collapsible "),
        MultiFieldPanel([
            PageChooserPanel('carousel_0'),
            PageChooserPanel('carousel_1'),
            PageChooserPanel('carousel_2'),
            PageChooserPanel('carousel_3'),
            PageChooserPanel('carousel_4'),
            PageChooserPanel('carousel_5'),
            PageChooserPanel('carousel_6'),
            PageChooserPanel('carousel_7'),
            PageChooserPanel('carousel_8'),
            PageChooserPanel('carousel_9'),
        ], "Carousel"),
        MultiFieldPanel([
            FieldPanel('in_focus_title'),
            FieldPanel('in_focus_link'),
            FieldPanel('in_focus_link_text'),
            PageChooserPanel('in_focus_page1'),
            PageChooserPanel('in_focus_page2'),
        ], "In Focus"),
        FieldPanel('talking_album'),
        FieldPanel('photo_album'),
        FieldPanel('video'),
        FieldPanel('language'),
    ]

    base_form_class = HomePageAdminForm

    def __str__(self):
        return _("HomePage")

    def get_context(self, request, *args, **kwargs):
        category1 = Category.objects.get(slug="resource-conflicts")
        category2 = Category.objects.get(slug="adivasis")
        category3 = Category.objects.get(slug="dalits")
        category4 = Category.objects.get(slug="sports-games")
        return {
            'talking_album': {
                'image': self.talking_album.slides.first().image,
                'count': self.talking_album.slides.count(),
                'photographers': self.talking_album.photographers,
                'section_model': self.talking_album,
            },
            'photo_album': {
                'image': self.photo_album.slides.first().image,
                'count': self.photo_album.slides.count(),
                'photographers': self.photo_album.photographers,
                'section_model': self.photo_album,
            },
            'video': {
                'image': self.video.featured_image,
                'photographers': self.video.authors.all(),
                'section_model': self.video,
            },
            'page':
            self,
            'categories': [category1, category2, category3, category4],
            'translations':
            get_translations_for_page(self),
            'translations_for_infocus_article1':
            get_translations_for_page(self.in_focus_page1.specific),
            'translations_for_infocus_article2':
            get_translations_for_page(self.in_focus_page2.specific),
            'current_page':
            'home-page',
            'request':
            request,
        }

    def carousel(self):
        items = []
        for ii in range(10):
            item = getattr(self, "carousel_{0}".format(ii))
            if item:
                items.append(item)
        return items

    def get_absolute_url(self):
        return reverse("home-page")
예제 #29
0
class RightSidebarPage(MenuPage):
    body = StreamField(MyStreamBlock(), blank=True)

    content_panels = Page.content_panels + [
        StreamFieldPanel('body'),
    ]
예제 #30
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)

    # 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()),
        ('contact', organisms.MainContactInfo()),
        ('sidebar_contact', organisms.SidebarContactInfo()),
        ('rss_feed', molecules.RSSFeed()),
        ('social_media', molecules.SocialMedia()),
    ],
                           blank=True)

    # Panels
    sidefoot_panels = [
        StreamFieldPanel('sidefoot'),
    ]

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

    # 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 generate_view_more_url(self, request):
        from ..forms import ActivityLogFilterForm
        activity_log = CFGOVPage.objects.get(slug='activity-log').specific
        form = ActivityLogFilterForm(parent=activity_log,
                                     hostname=request.site.hostname)
        available_tags = [
            tag[0] for name, tags in form.fields['topics'].choices
            for tag in tags
        ]
        tags = []
        index = util.get_form_id(activity_log)
        for tag in self.tags.names():
            if tag in available_tags:
                tags.append('filter%s_topics=' % index +
                            urllib.quote_plus(tag))
        tags = '&'.join(tags)
        return get_protected_url({'request': request},
                                 activity_log) + '?' + tags

    def related_posts(self, block, hostname):
        from . import AbstractFilterPage
        related = {}
        queries = {}
        query = models.Q(('tags__name__in', self.tags.names()))
        search_types = [
            ('blog', 'posts', 'Blog', query),
            ('newsroom', 'newsroom', 'Newsroom', query),
            ('events', 'events', 'Events', query),
        ]
        for parent_slug, search_type, search_type_name, search_query in search_types:
            try:
                parent = Page.objects.get(slug=parent_slug)
                search_query &= Page.objects.child_of_q(parent)
                if 'specific_categories' in block.value:
                    specific_categories = ref.related_posts_category_lookup(
                        block.value['specific_categories'])
                    choices = [
                        c[0] for c in ref.choices_for_page_type(parent_slug)
                    ]
                    categories = [
                        c for c in specific_categories if c in choices
                    ]
                    if categories:
                        search_query &= Q(('categories__name__in', categories))
                if parent_slug == 'events':
                    try:
                        parent_slug = 'archive-past-events'
                        parent = Page.objects.get(slug=parent_slug)
                        q = (Page.objects.child_of_q(parent) & query)
                        if 'specific_categories' in block.value:
                            specific_categories = ref.related_posts_category_lookup(
                                block.value['specific_categories'])
                            choices = [
                                c[0]
                                for c in ref.choices_for_page_type(parent_slug)
                            ]
                            categories = [
                                c for c in specific_categories if c in choices
                            ]
                            if categories:
                                q &= Q(('categories__name__in', categories))
                        search_query |= q
                    except Page.DoesNotExist:
                        print 'archive-past-events does not exist'
                queries[search_type_name] = search_query
            except Page.DoesNotExist:
                print parent_slug, 'does not exist'
        for parent_slug, search_type, search_type_name, search_query in search_types:
            if 'relate_%s' % search_type in block.value \
                    and block.value['relate_%s' % search_type]:
                related[search_type_name] = \
                    AbstractFilterPage.objects.live_shared(hostname).filter(
                        queries[search_type_name]).distinct().exclude(
                        id=self.id).order_by('-date_published')[:block.value['limit']]

        # Return a dictionary of lists of each type when there's at least one
        # hit for that type.
        return {
            search_type: queryset
            for search_type, queryset in related.items() if queryset
        }

    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:
                return [
                    util.get_appropriate_page_version(request, ancestor)
                    for ancestor in ancestors[i + 1:]
                ]
        return []

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

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

    def get_next_appropriate_siblings(self, hostname, inclusive=False):
        return self.get_appropriate_siblings(
            hostname=hostname,
            inclusive=inclusive).filter(path__gte=self.path).order_by('path')

    def get_prev_appropriate_siblings(self, hostname, inclusive=False):
        return self.get_appropriate_siblings(
            hostname=hostname,
            inclusive=inclusive).filter(path__lte=self.path).order_by('-path')

    @property
    def status_string(self):
        page = CFGOVPage.objects.get(id=self.id)
        if page.expired:
            return _("expired")
        elif page.approved_schedule:
            return _("scheduled")
        elif page.live and page.shared:
            if page.has_unpublished_changes:
                if page.has_unshared_changes:
                    for revision in page.revisions.order_by(
                            '-created_at', '-id'):
                        content = json.loads(revision.content_json)
                        if content['shared']:
                            if content['live']:
                                return _('live + draft')
                            else:
                                return _('live + (shared + draft)')
                else:
                    return _("live + shared")
            else:
                return _("live")
        elif page.shared:
            if page.has_unshared_changes:
                return _("shared + draft")
            else:
                return _("shared")
        else:
            return _("draft")

    def sharable_pages(self):
        """
        Return a queryset of the pages that this user has permission to share.
        """
        # Deal with the trivial cases first...
        if not self.user.is_active:
            return Page.objects.none()
        if self.user.is_superuser:
            return Page.objects.all()

        sharable_pages = Page.objects.none()

        for perm in self.permissions.filter(permission_type='share'):
            # User has share permission on any subpage of perm.page
            # (including perm.page itself).
            sharable_pages |= Page.objects.descendant_of(perm.page,
                                                         inclusive=True)

        return sharable_pages

    def can_share_pages(self):
        """Return True if the user has permission to publish any pages"""
        return self.sharable_pages().exists()

    def route(self, request, path_components):
        if path_components:
            # Request is for a child of this page.
            child_slug = path_components[0]
            remaining_components = path_components[1:]

            try:
                subpage = self.get_children().get(slug=child_slug)
            except Page.DoesNotExist:
                raise Http404

            return subpage.specific.route(request, remaining_components)

        else:
            # Request is for this very page.
            page = util.get_appropriate_page_version(request, self)
            if page:
                return RouteResult(page)
            raise Http404

    def permissions_for_user(self, user):
        """
        Return a CFGOVPagePermissionTester object defining what actions the
        user can perform on this page
        """
        user_perms = CFGOVUserPagePermissionsProxy(user)
        return user_perms.for_page(self)

    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
    # 'template' is used as the key for front-end consistency
    def add_page_js(self, js):
        js['template'] = []

    # Retrieves the stream values on a page from it's Streamfield
    def _get_streamfield_blocks(self):
        lst = [
            value for key, value in vars(self).iteritems()
            if type(value) is StreamValue
        ]
        return list(chain(*lst))

    # Gets the JS from the Streamfield data
    def _add_streamfield_js(self, js):
        # Create a dictionary with keys ordered organisms, molecules, then atoms
        for child in self._get_streamfield_blocks():
            self._add_block_js(child.block, js)

    # Recursively search the blocks and classes for declared Media.js
    def _add_block_js(self, block, js):
        self._assign_js(block, js)
        if issubclass(type(block), blocks.StructBlock):
            for child in block.child_blocks.values():
                self._add_block_js(child, js)
        elif issubclass(type(block), blocks.ListBlock):
            self._add_block_js(block.child_block, js)

    # Assign the Media js to the dictionary appropriately
    def _assign_js(self, obj, js):
        try:
            if hasattr(obj.Media, 'js'):
                for key in js.keys():
                    if obj.__module__.endswith(key):
                        js[key] += obj.Media.js
                if not [
                        key
                        for key in js.keys() if obj.__module__.endswith(key)
                ]:
                    js.update({'other': obj.Media.js})
        except:
            pass

    # Returns all the JS files specific to this page and it's current Streamfield's blocks
    @property
    def media(self):
        js = OrderedDict()
        for key in ['template', 'organisms', 'molecules', 'atoms']:
            js.update({key: []})
        self.add_page_js(js)
        self._add_streamfield_js(js)
        for key, js_files in js.iteritems():
            js[key] = OrderedDict.fromkeys(js_files).keys()
        return js