Exemple #1
0
class AboutSnippet(models.Model):
    title = models.CharField(default='', max_length=50)
    header_text = fields.RichTextField(blank=True)
    body_title = models.CharField(verbose_name='Title',
                                  blank=True,
                                  default='',
                                  max_length=50)
    body_text = fields.RichTextField(verbose_name='Text', blank=True)
    body_image = models.ForeignKey('wagtailimages.Image',
                                   null=True,
                                   blank=True,
                                   on_delete=models.SET_NULL,
                                   related_name='+',
                                   verbose_name='Image')
    info_title = models.CharField(verbose_name='Title',
                                  blank=True,
                                  default='',
                                  max_length=50)
    info_text = fields.RichTextField(verbose_name='Text', blank=True)
    details_title = models.CharField(verbose_name='Title',
                                     blank=True,
                                     default='',
                                     max_length=50)
    details = fields.StreamField([
        (
            'section',
            InputTextContentBlock(),
        ),
    ],
                                 null=True,
                                 blank=True,
                                 verbose_name='Blocks')
    collaborators_title = models.CharField(verbose_name='Title',
                                           blank=True,
                                           default='',
                                           max_length=50)
    collaborators_text = fields.RichTextField(verbose_name='Text', blank=True)
    collaborators_cta_text = models.CharField(verbose_name='CTA Text',
                                              blank=True,
                                              default='',
                                              max_length=50)
    collaborators_cta_link = models.URLField(verbose_name='CTA Link',
                                             blank=True,
                                             default='')
    collaborators = fields.StreamField([
        (
            'info',
            CardContentBlock(),
        ),
    ],
                                       null=True,
                                       blank=True)
    bottom_content_block = fields.StreamField([
        (
            'cta',
            BottomCTAContentBlock(),
        ),
    ],
                                              null=True,
                                              blank=True)

    panels = [
        FieldPanel('title'),
        FieldPanel('header_text'),
        MultiFieldPanel([
            FieldPanel('body_title'),
            FieldPanel('body_text'),
            ImageChooserPanel('body_image'),
        ],
                        heading='Body'),
        MultiFieldPanel([
            FieldPanel('info_title'),
            FieldPanel('info_text'),
        ],
                        heading='Info'),
        MultiFieldPanel([
            FieldPanel('details_title'),
            StreamFieldPanel('details'),
        ],
                        heading='Details'),
        MultiFieldPanel([
            FieldPanel('collaborators_title'),
            FieldPanel('collaborators_text'),
            FieldPanel('collaborators_cta_text'),
            FieldPanel('collaborators_cta_link'),
            StreamFieldPanel('collaborators'),
        ],
                        heading='Collaborators Section'),
        StreamFieldPanel('bottom_content_block'),
    ]

    class Meta:
        verbose_name_plural = 'About Snippets'

    def __str__(self):
        return self.title
Exemple #2
0
class StandardPage(Page):
    """
    This is a standard page style can be used for a range of static “content”
    pages on the site. Using StreamField it can allow for adaptable content
    """

    search_fields = Page.search_fields + [
        # Defining what fields the search catches
        index.SearchField('introduction'),
        index.SearchField('body'),
    ]

    date = models.DateField("Post date")

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

    # Note below that standard blocks use 'help_text' for supplementary text
    # rather than 'label' as with StreamField
    introduction = models.TextField(
        blank=True,
        help_text="Text to show at the top of the individual page"
        )

    # Using CharField for little reason other than showing a different input type
    # Wagtail allows you to use any field type Django follows, so you can use anything from
    # https://docs.djangoproject.com/en/1.9/ref/models/fields/#field-types
    listing_introduction = models.CharField(
        max_length=250,
        blank=True,
        help_text="Text shown on listing pages, if empty will show 'Introduction' field content"
        )

    # Note below we're calling StreamField from another location. The
    # `StandardBlock` class is a shared asset across the site. It is defined in
    # core > blocks.py. It is just as 'correct' to define the StreamField
    # directly within the model, but this method aids consistency.
    body = StreamField(StandardBlock(), blank=True)

    def get_context(self, request):
        # This is display view - I think - though I'm less show about what it's
        # *actually* doing
        context = super(StandardPage, self).get_context(request)
        context['children'] = Page.objects.live().in_menu().child_of(self)
        return context

    content_panels = Page.content_panels + [
        # The content panels are displaying the components of content we defined
        # in the StandardPage class above. If you add something to the class and
        # want it to appear for the editor you have to define it here too
        # A full list of the panel types you can use is at
        # http://docs.wagtail.io/en/latest/reference/pages/panels.html
        # If you add a different type of panel ensure you've imported it from
        # wagtail.wagtailadmin.edit_handlers in the `From` statements at the top
        # of the model
        FieldPanel('date'),
        ImageChooserPanel('image'),
        FieldPanel('introduction'),
        FieldPanel('listing_introduction'),
        StreamFieldPanel('body')
    ]
Exemple #3
0
class SEOSettings(BaseSetting):
    """Set site name and description for use with structured data."""

    organization_short_name = models.CharField(
        max_length=255,
        help_text='The actual name of your local (ie. Philly DSA')
    organization_full_name = models.CharField(
        max_length=500,
        help_text=
        'The actual name of your local (ie. Philadelphia Local of the Democratic [etc])'
    )
    address_street = models.CharField(
        max_length=255,
        help_text="Street address for local (if any)",
        blank=True)
    address_city = models.CharField(max_length=255, blank=True)
    address_state = models.CharField(max_length=255, blank=True)
    address_zip_code = models.CharField(max_length=5, blank=True)

    logo = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text=
        'The URL of the logo of your local. Ideally, should be 1200x1200')
    logo_wide = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text=
        'Wide-format logo URL. Should be less than 60px high and 600px wide.')
    description = models.CharField(
        max_length=500,
        help_text=
        'A short description of your local. City, what you are trying to achieve, etc.',
        blank=True)
    keywords = models.CharField(
        max_length=1500,
        help_text='Comma separated list of keywords describing your website.',
        blank=True)
    google_site_verification_key = models.CharField(
        max_length=255,
        help_text='Verification key for Google Webmaster Tools.',
        blank=True)
    google_analytics_key = models.CharField(
        max_length=255,
        help_text=
        'Google Analytics Key. Should be something like UA-101255774-1.',
        blank=True)

    panels = [
        MultiFieldPanel(heading="Chapter Information",
                        children=[
                            FieldPanel('organization_short_name'),
                            FieldPanel('organization_full_name'),
                            FieldPanel('description'),
                        ]),
        MultiFieldPanel(heading="Address Information",
                        children=[
                            FieldPanel('address_street'),
                            FieldPanel('address_city'),
                            FieldPanel('address_state'),
                            FieldPanel('address_zip_code'),
                        ]),
        ImageChooserPanel('logo'),
        ImageChooserPanel('logo_wide'),
        MultiFieldPanel(heading='Google Keys & Data',
                        children=[
                            FieldPanel('keywords'),
                            FieldPanel('google_site_verification_key'),
                            FieldPanel('google_analytics_key'),
                        ])
    ]
Exemple #4
0
class Answer(models.Model):
    last_user = models.ForeignKey(User, blank=True, null=True)
    category = models.ManyToManyField(
        'Category',
        blank=True,
        help_text=("Categorize this answer. "
                   "Avoid putting into more than one category."))
    question = models.TextField(blank=True)
    statement = models.TextField(
        blank=True,
        help_text=(
            "(Optional) Use this field to rephrase the question title as "
            "a statement. Use only if this answer has been chosen to appear "
            "on a money topic portal (e.g. /consumer-tools/debt-collection)."))
    snippet = RichTextField(
        blank=True,
        help_text=(
            "Optional answer intro, 180-200 characters max. "
            "Avoid adding links, images, videos or other rich text elements."))
    answer = RichTextField(
        blank=True,
        help_text=(
            "Do not use H2 or H3 to style text. Only use the HTML Editor "
            "for troubleshooting. To style tips, warnings and notes, "
            "select the content that will go inside the rule lines "
            "(so, title + paragraph) and click the Pencil button "
            "to style it. Click again to unstyle the tip."))
    slug = models.SlugField(max_length=255, blank=True)
    featured = models.BooleanField(
        default=False,
        help_text=("Check to make this one of two featured answers "
                   "on the landing page."))
    featured_rank = models.IntegerField(blank=True, null=True)

    question_es = models.TextField(blank=True, verbose_name="Spanish question")
    snippet_es = RichTextField(
        blank=True,
        help_text=("Do not use this field. "
                   "It is not currently displayed on the front end."),
        verbose_name="Spanish snippet")
    answer_es = RichTextField(
        blank=True,
        verbose_name="Spanish answer",
        help_text=(
            "Do not use H2 or H3 to style text. Only use the HTML Editor "
            "for troubleshooting. Also note that tips styling "
            "(the Pencil button) does not display on the front end."))
    slug_es = models.SlugField(max_length=255,
                               blank=True,
                               verbose_name="Spanish slug")
    search_tags = models.CharField(
        max_length=1000,
        blank=True,
        help_text="Search words or phrases, separated by commas")
    search_tags_es = models.CharField(
        max_length=1000,
        blank=True,
        help_text="Spanish search words or phrases, separated by commas")
    update_english_page = models.BooleanField(
        default=False,
        verbose_name="Send to English page for review",
        help_text=(
            "Check this box to push your English edits "
            "to the page for review. This does not publish your edits."))
    update_spanish_page = models.BooleanField(
        default=False,
        verbose_name="Send to Spanish page for review",
        help_text=(
            "Check this box to push your Spanish edits "
            "to the page for review. This does not publish your edits."))
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    last_edited = models.DateField(
        blank=True,
        null=True,
        help_text="Change the date to today "
        "if you edit an English question, snippet or answer.",
        verbose_name="Last edited English content")
    last_edited_es = models.DateField(
        blank=True,
        null=True,
        help_text="Change the date to today "
        "if you edit a Spanish question, snippet or answer.",
        verbose_name="Last edited Spanish content")
    subcategory = models.ManyToManyField(
        'SubCategory',
        blank=True,
        help_text=("Choose only subcategories that belong "
                   "to one of the categories checked above."))
    audiences = models.ManyToManyField(
        'Audience',
        blank=True,
        help_text="Tag any audiences that may be interested in the answer.")
    next_step = models.ForeignKey(
        NextStep,
        blank=True,
        null=True,
        help_text=("Formerly known as action items or upsell items."
                   "On the web page, these are labeled as "
                   "'Explore related resources.'"))
    related_questions = models.ManyToManyField('self',
                                               symmetrical=False,
                                               blank=True,
                                               related_name='related_question',
                                               help_text='Maximum of 3')
    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. If no image is selected, this '
            'page\'s category image will be used. '
            'Recommended size: 1200w x 630h. '
            'Maximum size: 4096w x 4096h.'))

    panels = [
        MultiFieldPanel([
            FieldRowPanel(
                [FieldPanel('update_english_page'),
                 FieldPanel('last_edited')]),
            FieldRowPanel([
                FieldPanel('update_spanish_page'),
                FieldPanel('last_edited_es')
            ])
        ],
                        heading="Workflow fields -- check before saving",
                        classname="collapsible"),
        MultiFieldPanel([
            FieldPanel('question', classname="title"),
            FieldPanel('statement', classname="title"),
            FieldPanel('snippet', classname="full"),
            FieldPanel('answer', classname="full")
        ],
                        heading="English",
                        classname="collapsible"),
        MultiFieldPanel([
            FieldPanel('question_es', classname="title"),
            FieldPanel('snippet_es', classname="full"),
            FieldPanel('answer_es', classname="full")
        ],
                        heading="Spanish",
                        classname="collapsible"),
        MultiFieldPanel([
            FieldRowPanel(
                [FieldPanel('featured'),
                 FieldPanel('featured_rank')]),
            FieldPanel('audiences', widget=forms.CheckboxSelectMultiple),
            FieldPanel('next_step'),
            FieldPanel('category', widget=forms.CheckboxSelectMultiple),
            FieldPanel('subcategory', widget=forms.CheckboxSelectMultiple),
            FieldPanel('related_questions',
                       widget=forms.SelectMultiple,
                       classname="full"),
            FieldPanel('search_tags'),
            FieldPanel('search_tags_es'),
            ImageChooserPanel('social_sharing_image')
        ],
                        heading="Metadata",
                        classname="collapsible"),
    ]

    class Meta:
        ordering = ['-id']

    def __str__(self):
        return "{} {}".format(self.id, self.slug)

    @property
    def available_subcategory_qs(self):
        return SubCategory.objects.filter(parent__in=self.category.all())

    @property
    def english_page(self):
        return self.answer_pages.filter(language='en').first()

    @property
    def spanish_page(self):
        return self.answer_pages.filter(language='es').first()

    @property
    def answer_text(self):
        """Unescapes and removes html tags from answer fields"""
        unescaped = ("{} {}".format(html_parser.unescape(self.snippet),
                                    html_parser.unescape(self.answer)))
        return html.strip_tags(unescaped).strip()

    @property
    def answer_text_es(self):
        """Unescapes and removes html tags from Spanish answer fields"""
        unescaped = ("{} {}".format(html_parser.unescape(self.snippet_es),
                                    html_parser.unescape(self.answer_es)))
        return html.strip_tags(unescaped).strip()

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

    def cleaned_questions(self):
        cleaned_terms = html_parser.unescape(self.question)
        return [html.strip_tags(cleaned_terms).strip()]

    def cleaned_questions_es(self):
        cleaned_terms = html_parser.unescape(self.question_es)
        return [html.strip_tags(cleaned_terms).strip()]

    def category_text(self):
        if self.category.all():
            return [cat.name for cat in self.category.all()]
        else:
            return ''

    def category_text_es(self):
        if self.category.all():
            return [cat.name_es for cat in self.category.all()]
        else:
            return ''

    def audience_strings(self):
        return [audience.name for audience in self.audiences.all()]

    @staticmethod
    def clean_tag_list(taglist):
        return [
            tag.replace('"', '').strip() for tag in taglist.split(',')
            if tag.replace('"', '').strip()
        ]

    @cached_property
    def clean_tags(self):
        return self.clean_tag_list(self.search_tags)

    @cached_property
    def clean_tags_es(self):
        return self.clean_tag_list(self.search_tags_es)

    @classmethod
    def valid_tags(cls, language='en'):
        """
        Search tags are arbitrary and messy. This function serves 2 purposes:
        - Assemble a whitelist of tags that are safe for search.
        - Exclude tags that are attached to only one answer.
        Tags are useless until they can be used to collect at least 2 answers.

        This method returns a dict {'valid_tags': [], tag_map: {}}
        valid_tags is an alphabetical list of valid tags.
        tag_map is a dictionary mapping tags to questions.
        """
        cleaned = []
        tag_map = {}
        if language == 'es':
            for a in cls.objects.all():
                cleaned += a.clean_tags_es
                for tag in a.clean_tags_es:
                    if tag not in tag_map:
                        tag_map[tag] = [a]
                    else:
                        tag_map[tag].append(a)
        else:
            for a in cls.objects.all():
                cleaned += a.clean_tags
                for tag in a.clean_tags:
                    if tag not in tag_map:
                        tag_map[tag] = [a]
                    else:
                        tag_map[tag].append(a)
        tag_counter = Counter(cleaned)
        valid = sorted(tup[0] for tup in tag_counter.most_common()
                       if tup[1] > 1)
        return {'valid_tags': valid, 'tag_map': tag_map}

    def has_live_page(self):
        if not self.answer_pages.all():
            return False
        for page in self.answer_pages.all():
            if page.live:
                return True
        return False

    def create_or_update_page(self, language=None):
        """Create or update an English or Spanish Answer page"""
        from .pages import AnswerPage
        english_parent = Page.objects.get(slug=ENGLISH_PARENT_SLUG).specific
        spanish_parent = Page.objects.get(slug=SPANISH_PARENT_SLUG).specific
        if language == 'en':
            _parent = english_parent
            _slug = self.slug
            _question = self.question
            # _snippet = self.snippet
            # _answer = self.answer
            # _statement = self.statement
            _last_edited = self.last_edited
            _search_tags = self.search_tags
        elif language == 'es':
            _parent = spanish_parent
            _slug = self.slug_es
            _question = self.question_es
            # _snippet = self.snippet_es
            # _answer = self.answer_es
            # _statement = ''
            _last_edited = self.last_edited_es
            _search_tags = self.search_tags_es
        else:
            raise ValueError('unsupported language: "{}"'.format(language))
        try:
            base_page = AnswerPage.objects.get(language=language,
                                               answer_base=self)
        except ObjectDoesNotExist:
            base_page = get_or_create_page(apps,
                                           'ask_cfpb',
                                           'AnswerPage',
                                           '{}-{}-{}'.format(
                                               _question[:244], language,
                                               self.id),
                                           _slug,
                                           _parent,
                                           show_in_menus=True,
                                           language=language,
                                           answer_base=self)
            base_page.save_revision(user=self.last_user)
        _page = base_page.get_latest_revision_as_page()
        # _page.question = _question
        # _page.answer = _answer
        # _page.snippet = _snippet
        # _page.statement = _statement
        _page.last_edited = _last_edited
        _page.search_tags = _search_tags
        _page.title = '{}-{}-{}'.format(_question[:244], language, self.id)
        _page.live = False
        _page.has_unpublished_changes = True
        stream_block = _page.content.stream_block
        _page.content = StreamValue(stream_block,
                                    get_feedback_stream_value(_page),
                                    is_lazy=True)
        _page.save_revision(user=self.last_user)
        return _page

    def create_or_update_pages(self):
        counter = 0
        if self.answer:
            counter += 1
            self.create_or_update_page(language='en')
        if self.answer_es:
            counter += 1
            self.create_or_update_page(language='es')
        return counter

    def save(self, skip_page_update=False, *args, **kwargs):
        if not self.id:
            super(Answer, self).save(*args, **kwargs)
            self.save(skip_page_update=skip_page_update)
        else:
            if self.answer:
                self.slug = "{}-en-{}".format(
                    generate_short_slug(self.question), self.id)
            else:
                self.slug = "slug-en-{}".format(self.id)
            if self.answer_es:
                self.slug_es = "{}-es-{}".format(
                    generate_short_slug(self.question_es), self.id)
            else:
                self.slug_es = "slug-es-{}".format(self.id)
            super(Answer, self).save(*args, **kwargs)
            if skip_page_update is False:
                if self.update_english_page:
                    self.create_or_update_page(language='en')
                if self.update_spanish_page:
                    self.create_or_update_page(language='es')
Exemple #5
0
class HomePage(Page, TranslatablePageMixin):

    header_img = models.ForeignKey('wagtailimages.Image',
                                   null=True,
                                   blank=True,
                                   on_delete=models.SET_NULL,
                                   related_name='+')
    header_bg = models.ForeignKey('wagtailimages.Image',
                                  null=True,
                                  blank=True,
                                  on_delete=models.SET_NULL,
                                  related_name='+')
    header_title = models.CharField(null=True, blank=True, max_length=200)
    header_subtitle = models.CharField(null=True, blank=True, max_length=200)

    # About Me Section
    about_title = models.CharField(null=True, blank=True, max_length=300)
    about_text = RichTextField(blank=True)

    # My Resume
    mr_title = models.CharField(null=True, blank=True, max_length=300)
    mr_text = RichTextField(blank=True)

    # work XP and studies
    wx_title = models.CharField(null=True, blank=True, max_length=300)
    studies_title = models.CharField(null=True, blank=True, max_length=300)

    # services
    services_title = models.CharField(blank=True, max_length=250)

    # skills
    skill_title = models.CharField(blank=True, max_length=250)

    # Position
    position = models.CharField(max_length=255, blank=True)

    content_panels = Page.content_panels + [
        MultiFieldPanel(TranslatablePageMixin.panels, 'Language links'),
        MultiFieldPanel([
            InlinePanel('socials', label="Socials Media"),
            ImageChooserPanel('header_img'),
            ImageChooserPanel('header_bg'),
            FieldPanel('header_title'),
            FieldPanel('header_subtitle'),
        ],
                        heading="Header section",
                        classname="collapsible collapsed"),
        MultiFieldPanel([
            FieldPanel('about_title'),
            FieldPanel('about_text', classname="full"),
        ],
                        heading="About section",
                        classname="collapsible collapsed"),
        MultiFieldPanel([
            FieldPanel('wx_title'),
            InlinePanel('time_line_work', label="Work Experiences"),
            FieldPanel('studies_title'),
            InlinePanel('time_line_study', label="Studies"),
        ],
                        heading="Experience section",
                        classname="collapsible collapsed"),
        MultiFieldPanel([
            FieldPanel('services_title'),
            InlinePanel('services', label="Services"),
        ],
                        heading="Experience section",
                        classname="collapsible collapsed"),
        MultiFieldPanel([
            FieldPanel('skill_title'),
            InlinePanel('skills', label="Skills"),
        ],
                        heading="Experience section",
                        classname="collapsible collapsed"),
        MapFieldPanel('position', latlng=True)
    ]
Exemple #6
0
class Farm(models.Model):
    title = models.CharField(max_length=255, unique=True)
    about = RichTextField(blank=True)
    image = models.ForeignKey('wagtailimages.Image',
                              null=True,
                              blank=True,
                              on_delete=models.SET_NULL,
                              related_name='+')

    person = models.CharField(max_length=255,
                              blank=True,
                              verbose_name=_('Name of responsible contact'))
    phone = models.CharField(max_length=255, blank=True)
    mobile = models.CharField(max_length=255, blank=True)
    email = models.CharField(max_length=255, blank=True)
    website = models.URLField(blank=True)
    address = models.CharField(max_length=255, blank=True)
    longitude = models.CharField(max_length=50, blank=True)
    latitude = models.CharField(max_length=50, blank=True)

    updated = models.DateTimeField(auto_now=True, editable=False)
    published = models.DateTimeField(auto_now_add=True, editable=False)

    is_producer = models.BooleanField(
        default=True,
        verbose_name='Producer',
        help_text=_('Is this a producer (and not only distributor)?'))
    distributors = models.ManyToManyField("self", blank=True)

    labels = models.ManyToManyField(Label, blank=True)
    region = models.ForeignKey(Region,
                               null=True,
                               blank=True,
                               on_delete=models.PROTECT)
    datasource = models.ForeignKey(Datasource,
                                   null=True,
                                   blank=True,
                                   on_delete=models.PROTECT)

    panels = [
        FieldPanel('title'),
        FieldPanel('about'),
        ImageChooserPanel('image'),
        MultiFieldPanel(
            [
                FieldPanel('person'),
                FieldPanel('phone'),
                FieldPanel('mobile'),
                FieldPanel('email'),
                FieldPanel('website'),
                FieldPanel('address'),
                FieldPanel('longitude'),
                FieldPanel('latitude'),
            ],
            heading="Contact",
            classname="collapsible collapsed",
        ),
        MultiFieldPanel(
            [
                FieldPanel('region'),
                FieldPanel('labels'),
            ],
            heading="Details",
            classname="col5",
        ),
        MultiFieldPanel(
            [
                FieldPanel('is_producer'),
                FieldPanel('distributors'),
                FieldPanel('datasource'),
            ],
            heading="Connections",
            classname="col7",
        ),
    ]

    api_fields = [
        APIField('title'),
        APIField('about'),
        APIField('image_thumb',
                 serializer=ImageRenditionField('width-160', source='image')),
        APIField('image_full',
                 serializer=ImageRenditionField('width-800', source='image')),
        APIField('produce', serializer=ProduceRenditionField()),
        APIField('labels', serializer=LabelRenditionField()),
        APIField('region', serializer=RegionRenditionField()),
        APIField('distributors')
    ]
    api_meta_fields = [
        'person',
        'phone',
        'mobile',
        'email',
        'website',
        'address',
        'longitude',
        'latitude',
        'is_producer',
    ]

    def __str__(self):
        return self.title
Exemple #7
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()

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

    # 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 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 BAH page breadcrumbs.
                # TODO: Remove this when BAH 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)
Exemple #8
0
class BirthdayPage(Page):
    button_text_max = 128
    color_help_text = '6 digit CSS color code.'
    color_max_length = 6
    embed_help_text = 'Raw HTML embed code for signup form, etc.'
    section_2_help_text = 'Use bold for large text.'
    title_max_length = 128

    header_border_color = models.CharField(
        blank=True,
        help_text=color_help_text,
        max_length=color_max_length,
        null=True,
    )
    header_border_image = models.ForeignKey('wagtailimages.Image',
                                            blank=True,
                                            null=True,
                                            on_delete=models.SET_NULL,
                                            related_name='+')
    header_button_new_window = models.BooleanField(
        default=False, help_text='Open link in new window?')
    header_button_text = models.CharField(max_length=button_text_max)
    header_button_url = models.URLField()
    progress_bar_count = models.IntegerField(blank=True, null=True)
    progress_bar_display = models.BooleanField(default=True)
    progress_bar_goal = models.IntegerField(blank=True, null=True)
    progress_bar_goal_name = models.CharField(
        blank=True,
        max_length=title_max_length,
        null=True,
    )
    progress_bar_goal_new = models.IntegerField(blank=True, null=True)
    primary_content_background_color = models.CharField(
        blank=True,
        help_text=color_help_text,
        max_length=color_max_length,
        null=True,
    )
    primary_content_background_image = models.ForeignKey(
        'wagtailimages.Image',
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name='+')
    primary_content_body = RichTextField()
    primary_content_button_text = models.CharField(max_length=button_text_max)
    primary_content_embed_code = models.TextField(help_text=embed_help_text)
    primary_content_embed_layout = models.IntegerField(
        choices=[x.value for x in EmbedLayout],
        default=EmbedLayout.layout_16x9.value[0],
    )
    section_2_1_body = RichTextField(help_text=section_2_help_text)
    section_2_1_image = models.ForeignKey('wagtailimages.Image',
                                          blank=True,
                                          null=True,
                                          on_delete=models.SET_NULL,
                                          related_name='+')
    section_2_2_body = RichTextField(help_text=section_2_help_text)
    section_2_2_image = models.ForeignKey('wagtailimages.Image',
                                          blank=True,
                                          null=True,
                                          on_delete=models.SET_NULL,
                                          related_name='+')
    section_2_3_body = RichTextField(help_text=section_2_help_text)
    section_2_3_image = models.ForeignKey('wagtailimages.Image',
                                          blank=True,
                                          null=True,
                                          on_delete=models.SET_NULL,
                                          related_name='+')
    section_2_4_body = RichTextField(help_text=section_2_help_text)
    section_2_4_image = models.ForeignKey('wagtailimages.Image',
                                          blank=True,
                                          null=True,
                                          on_delete=models.SET_NULL,
                                          related_name='+')
    section_3_body = RichTextField()
    section_4_background_color = models.CharField(
        blank=True,
        help_text=color_help_text,
        max_length=color_max_length,
        null=True,
    )
    section_4_background_image = models.ForeignKey('wagtailimages.Image',
                                                   blank=True,
                                                   null=True,
                                                   on_delete=models.SET_NULL,
                                                   related_name='+')
    section_4_body = RichTextField()
    section_4_1_icon = models.ForeignKey('wagtailimages.Image',
                                         blank=True,
                                         null=True,
                                         on_delete=models.SET_NULL,
                                         related_name='+')
    section_4_1_title = models.CharField(max_length=title_max_length)
    section_4_2_icon = models.ForeignKey('wagtailimages.Image',
                                         blank=True,
                                         null=True,
                                         on_delete=models.SET_NULL,
                                         related_name='+')
    section_4_2_title = models.CharField(max_length=title_max_length)
    section_4_3_icon = models.ForeignKey('wagtailimages.Image',
                                         blank=True,
                                         null=True,
                                         on_delete=models.SET_NULL,
                                         related_name='+')
    section_4_3_title = models.CharField(max_length=title_max_length)
    section_4_4_icon = models.ForeignKey('wagtailimages.Image',
                                         blank=True,
                                         null=True,
                                         on_delete=models.SET_NULL,
                                         related_name='+')
    section_4_4_title = models.CharField(max_length=title_max_length)
    section_4_5_icon = models.ForeignKey('wagtailimages.Image',
                                         blank=True,
                                         null=True,
                                         on_delete=models.SET_NULL,
                                         related_name='+')
    section_4_5_title = models.CharField(max_length=title_max_length)
    section_4_6_icon = models.ForeignKey('wagtailimages.Image',
                                         blank=True,
                                         null=True,
                                         on_delete=models.SET_NULL,
                                         related_name='+')
    section_4_6_title = models.CharField(max_length=title_max_length)
    section_4_7_icon = models.ForeignKey('wagtailimages.Image',
                                         blank=True,
                                         null=True,
                                         on_delete=models.SET_NULL,
                                         related_name='+')
    section_4_7_title = models.CharField(max_length=title_max_length)
    section_4_8_icon = models.ForeignKey('wagtailimages.Image',
                                         blank=True,
                                         null=True,
                                         on_delete=models.SET_NULL,
                                         related_name='+')
    section_4_8_title = models.CharField(max_length=title_max_length)
    section_5_body = RichTextField()
    section_5_image = models.ForeignKey('wagtailimages.Image',
                                        blank=True,
                                        null=True,
                                        on_delete=models.SET_NULL,
                                        related_name='+')
    section_6_body = RichTextField()
    section_6_image = models.ForeignKey('wagtailimages.Image',
                                        blank=True,
                                        null=True,
                                        on_delete=models.SET_NULL,
                                        related_name='+')
    section_7_background_color = models.CharField(
        blank=True,
        help_text=color_help_text,
        max_length=color_max_length,
        null=True,
    )
    section_7_background_image = models.ForeignKey('wagtailimages.Image',
                                                   blank=True,
                                                   null=True,
                                                   on_delete=models.SET_NULL,
                                                   related_name='+')
    section_7_body = RichTextField()
    section_7_button_new_window = models.BooleanField(
        default=False, help_text='Open link in new window?')
    section_7_button_text = models.CharField(max_length=button_text_max)
    section_7_button_url = models.URLField()
    social_image = models.ForeignKey('wagtailimages.Image',
                                     blank=True,
                                     null=True,
                                     on_delete=models.SET_NULL,
                                     related_name='+')

    def _get_progress_bar_goal_current(self):
        if self.progress_bar_goal is None:
            return None
        elif self.progress_bar_goal_new is not None and (
                self.progress_bar_count is not None
        ) and self.progress_bar_count >= self.progress_bar_goal:
            return self.progress_bar_goal_new
        else:
            return self.progress_bar_goal
        return goal_current

    progress_bar_goal_current = property(_get_progress_bar_goal_current)

    def _get_progress_bar_goal_met(self):
        if self.progress_bar_count is not None and (self.progress_bar_goal
                                                    is not None):
            return self.progress_bar_count >= self.progress_bar_goal
        else:
            return False

    progress_bar_goal_met = property(_get_progress_bar_goal_met)

    def _get_progress_bar_percent(self):
        if self.progress_bar_count is not None and (
                self.progress_bar_goal_current is not None):
            return min(
                float(self.progress_bar_count) /
                float(self.progress_bar_goal_current), 1) * 100
        else:
            return None

    progress_bar_percent = property(_get_progress_bar_percent)

    content_panels = Page.content_panels + [
        MultiFieldPanel([
            FieldPanel('progress_bar_display'),
            FieldPanel('progress_bar_count'),
            FieldPanel('progress_bar_goal'),
            FieldPanel('progress_bar_goal_new'),
            FieldPanel('progress_bar_goal_name'),
        ],
                        heading="Progress Bar",
                        classname="collapsible"),
        MultiFieldPanel([
            FieldPanel('header_button_text'),
            FieldPanel('header_button_url'),
            FieldPanel('header_button_new_window'),
            ImageChooserPanel('header_border_image'),
            FieldPanel('header_border_color'),
        ],
                        heading="Header",
                        classname="collapsible"),
        MultiFieldPanel([
            FieldPanel('primary_content_body'),
            FieldPanel('primary_content_button_text'),
            FieldPanel('primary_content_embed_code'),
            FieldPanel('primary_content_embed_layout'),
            ImageChooserPanel('primary_content_background_image'),
            FieldPanel('primary_content_background_color'),
        ],
                        heading="Primary Content",
                        classname="collapsible"),
        MultiFieldPanel([
            FieldPanel('section_2_1_body'),
            ImageChooserPanel('section_2_1_image'),
            FieldPanel('section_2_2_body'),
            ImageChooserPanel('section_2_2_image'),
            FieldPanel('section_2_3_body'),
            ImageChooserPanel('section_2_3_image'),
            FieldPanel('section_2_4_body'),
            ImageChooserPanel('section_2_4_image'),
        ],
                        heading="Section 2",
                        classname="collapsible"),
        MultiFieldPanel([
            FieldPanel('section_3_body'),
        ],
                        heading="Section 3",
                        classname="collapsible"),
        MultiFieldPanel([
            FieldPanel('section_4_body'),
            ImageChooserPanel('section_4_background_image'),
            FieldPanel('section_4_background_color'),
            FieldPanel('section_4_1_title'),
            ImageChooserPanel('section_4_1_icon'),
            FieldPanel('section_4_2_title'),
            ImageChooserPanel('section_4_2_icon'),
            FieldPanel('section_4_3_title'),
            ImageChooserPanel('section_4_3_icon'),
            FieldPanel('section_4_4_title'),
            ImageChooserPanel('section_4_4_icon'),
            FieldPanel('section_4_5_title'),
            ImageChooserPanel('section_4_5_icon'),
            FieldPanel('section_4_6_title'),
            ImageChooserPanel('section_4_6_icon'),
            FieldPanel('section_4_7_title'),
            ImageChooserPanel('section_4_7_icon'),
            FieldPanel('section_4_8_title'),
            ImageChooserPanel('section_4_8_icon'),
        ],
                        heading="Section 4",
                        classname="collapsible"),
        MultiFieldPanel([
            FieldPanel('section_5_body'),
            ImageChooserPanel('section_5_image'),
        ],
                        heading="Section 5",
                        classname="collapsible"),
        MultiFieldPanel([
            FieldPanel('section_6_body'),
            ImageChooserPanel('section_6_image'),
        ],
                        heading="Section 6",
                        classname="collapsible"),
        MultiFieldPanel([
            FieldPanel('section_7_body'),
            FieldPanel('section_7_button_text'),
            FieldPanel('section_7_button_url'),
            FieldPanel('section_7_button_new_window'),
            ImageChooserPanel('section_7_background_image'),
            FieldPanel('section_7_background_color'),
        ],
                        heading="Section 7",
                        classname="collapsible"),
    ]
    parent_page_types = ['pages.IndexPage']
    promote_panels = Page.promote_panels + [ImageChooserPanel('social_image')]
    subpage_types = []
Exemple #9
0
class BlogPage(RoutablePageMixin, Page):
    sidebar_image = models.ForeignKey(
        'wagtailimages.Image',
        null=True, 
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
    )

    description = models.CharField(max_length=255, blank=True,)

    add_page_1 = models.CharField(max_length=255, blank=True, null=True,)
    link_to_page_1 = models.CharField(max_length=255, blank=True, null=True,)
    add_page_2 = models.CharField(max_length=255, blank=True, null=True,)
    link_to_page_2 = models.CharField(max_length=255, blank=True, null=True,)
    add_page_3 = models.CharField(max_length=255, blank=True, null=True,)
    link_to_page_3 = models.CharField(max_length=255, blank=True, null=True,)
    add_page_4 = models.CharField(max_length=255, blank=True, null=True,)
    link_to_page_4 = models.CharField(max_length=255, blank=True, null=True,)
    add_page_5 = models.CharField(max_length=255, blank=True, null=True,)
    link_to_page_5 = models.CharField(max_length=255, blank=True, null=True,)
    add_page_6 = models.CharField(max_length=255, blank=True, null=True,)
    link_to_page_6 = models.CharField(max_length=255, blank=True, null=True,)

    content_panels = Page.content_panels + [
        FieldPanel('description', classname="full"),
        ImageChooserPanel('sidebar_image'),
        FieldPanel('add_page_1'),
        FieldPanel('link_to_page_1'),
        FieldPanel('add_page_2'),
        FieldPanel('link_to_page_2'),
        FieldPanel('add_page_3'),
        FieldPanel('link_to_page_3'),
        FieldPanel('add_page_4'),
        FieldPanel('link_to_page_4'),
        FieldPanel('add_page_5'),
        FieldPanel('link_to_page_5'),
        FieldPanel('add_page_6'),
        FieldPanel('link_to_page_6'),
    ]

    def get_context(self, request, *args, **kwargs):
        context = super(BlogPage, self).get_context(request, *args, **kwargs)
        context['posts'] = self.posts
        context['blog_page'] = self
        context['search_type'] = getattr(self, 'search_type', "")
        context['search_term'] = getattr(self, 'search_term', "")
        return context

    def get_posts(self):
        return PostPage.objects.descendant_of(self).live().order_by('-date')

    @route(r'^(\d{4})/$')
    @route(r'^(\d{4})/(\d{2})/$')
    @route(r'^(\d{4})/(\d{2})/(\d{2})/$')
    def post_by_date(self, request, year, month=None, day=None, *args, **kwargs):
        self.posts = self.get_posts().filter(date__year=year)
        self.search_type = 'date'
        self.search_term = year
        if month:
            self.posts = self.posts.filter(date__month=month)
            df = DateFormat(date(int(year), int(month), 1))
            self.search_term = df.format('F Y')
        if day:
            self.posts = self.posts.filter(date__day=day)
            self.search_term = date_format(date(int(year), int(month), int(day)))
        return Page.serve(self, request, *args, **kwargs)

    @route(r'^(\d{4})/(\d{2})/(\d{2})/(.+)/$')
    def post_by_date_slug(self, request, year, month, day, slug, *args, **kwargs):
        post_page = self.get_posts().filter(slug=slug).first()
        if not post_page:
            raise Http404
        return Page.serve(post_page, request, *args, **kwargs)

    @route(r'^tag/(?P<tag>[-\w]+)/$')
    def post_by_tag(self, request, tag, *args, **kwargs):
        self.search_type = 'tag'
        self.search_term = tag
        self.posts = self.get_posts().filter(tags__slug=tag)
        return Page.serve(self, request, *args, **kwargs)

    @route(r'^category/(?P<category>[-\w]+)/$')
    def post_by_category(self, request, category, *args, **kwargs):
        self.search_type = 'category'
        self.search_term = category
        self.posts = self.get_posts().filter(categories__slug=category)
        return Page.serve(self, request, *args, **kwargs)

    @route(r'^$')
    def post_list(self, request, *args, **kwargs):
        self.posts = self.get_posts()
        return Page.serve(self, request, *args, **kwargs)

    @route(r'^search/$')
    def post_search(self, request, *args, **kwargs):
        search_query = request.GET.get('q', None)
        self.posts = self.get_posts()
        if search_query:
            self.posts = self.posts.filter(body__contains=search_query)
            self.search_term = search_query
            self.search_type = 'search'
        return Page.serve(self, request, *args, **kwargs)
Exemple #10
0
                              related_name='+')

    search_fields = Page.search_fields + (
        index.SearchField('blurb'),
        index.SearchField('body'),
    )

    def get_context(self, request, *args, **kwargs):
        return {
            'self': self,
            'request': request,
            'page_title': self.title,
        }

    def get_absolute_url(self):
        return self.full_url


NewsStory.content_panels = [
    FieldPanel('title', classname="full title"),
    FieldPanel('date'),
    FieldPanel('blurb', classname="full"),
    FieldPanel('body', classname="full"),
]

NewsStory.promote_panels = [
    MultiFieldPanel(Page.promote_panels, "Common page configuration"),
    ImageChooserPanel('image'),
    FieldPanel('tags'),
]
Exemple #11
0
class HeroVideoFields(models.Model):
    hero_video = models.ForeignKey(
        'utils.CustomMedia',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        help_text=
        "Short Hero Video to show on top of page. Recommended size 12Mb or under.",
        related_name='+')
    hero_fallback_image = models.ForeignKey(
        'images.CustomImage',
        null=True,
        blank=True,
        related_name='+',
        help_text="Hero Image to be used as fallback for video.",
        on_delete=models.SET_NULL)
    hero_strapline = models.TextField(
        blank=True,
        max_length=255,
        help_text=
        "Shows text over the hero. If no strapline is entered, no page title will show."
    )
    hero_strapline_hex = models.CharField(
        blank=True,
        max_length=7,
        help_text="Add valid hex to change colour of strapline.")

    link_page = models.ForeignKey(
        Page,
        blank=True,
        null=True,
        related_name='+',
        on_delete=models.SET_NULL,
        help_text="Optional page link as clickthrough for hero video.",
        verbose_name="Page Link")
    link_youtube = models.URLField(
        blank=True,
        help_text="Optional URL for a full length YouTube video goes here,\
                    which will open in a modal window.",
        verbose_name="YouTube Link")
    link_text = models.CharField(blank=True, max_length=255)

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

    content_panels = [
        MultiFieldPanel([
            MediaChooserPanel('hero_video'),
            ImageChooserPanel('hero_fallback_image'),
            FieldPanel('hero_strapline'),
            FieldPanel('hero_strapline_hex'),
            MultiFieldPanel([
                PageChooserPanel('link_page'),
                FieldPanel('link_youtube'),
                FieldPanel('link_text'),
            ], 'Hero Clickthrough Link')
        ], 'Hero Video'),
    ]

    def clean(self):
        from girleffect.utils.blocks import validate_hex

        if self.hero_strapline_hex:
            if not validate_hex(self.hero_strapline_hex):
                raise ValidationError(
                    {'hero_strapline_hex': _('Please enter a valid hex code')})

        # Validating if URL is a valid YouTube URL
        youtube_embed = self.link_youtube
        if youtube_embed:
            youtube_finder = OEmbedFinder(providers=[oembed_providers.youtube])
            if not youtube_finder.accept(youtube_embed):
                raise ValidationError(
                    {'link_youtube': _('Please supply a valid YouTube URL.')})
            else:
                try:
                    embed = get_embed(youtube_embed)
                    self.link_youtube_html = embed.html
                except EmbedException:
                    raise ValidationError(
                        {'link_youtube': _('Embed cannot be found.')})

        # Validating links
        populated_fields = []

        for link_field in [self.link_page, self.link_youtube]:
            if link_field:
                populated_fields.append(link_field)

        # Only only one or less fields can be selected
        if len(populated_fields) > 1:
            error_message = 'Please choose only one of Link Page or Link YouTube as destination.'
            raise ValidationError({
                'link_page': error_message,
                'link_youtube': error_message
            })

        # Link fields should have link text
        if len(populated_fields) >= 1 and not self.link_text:
            raise ValidationError({
                'link_text':
                'Link text is required if link destination has been selected'
            })

        return super(HeroVideoFields, self).clean()

    class Meta:
        abstract = True
Exemple #12
0
class Banner(Orderable):
    page = ParentalKey(
        HomePage,
        related_name='banners',
        on_delete=models.CASCADE,
        blank=False,
    )

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

    title_en = models.CharField(
        max_length=255,
        verbose_name=_('English banner title'),
        help_text=_('Enter the title to be shown on the banner.'),
        blank=True,
    )

    title_sv = models.CharField(
        max_length=255,
        verbose_name=_('Swedish banner title'),
        help_text=_('Enter the title to be shown on the banner.'),
        blank=True,
    )

    title = TranslatedField('title_en', 'title_sv')

    text_en = models.TextField(
        verbose_name=_('English banner text'),
        help_text=_('Enter a text to be shown on the banner.'),
        blank=True,
    )

    text_sv = models.TextField(
        verbose_name=_('Swedish banner text'),
        help_text=_('Enter a text to be shown on the banner.'),
        blank=True,
    )

    text = TranslatedField('text_en', 'text_sv')

    link = models.URLField(
        verbose_name=_('Button URL'),
        blank=True,
    )

    button_en = models.TextField(
        verbose_name=_('English button text'),
        help_text=_('Enter the text to be displayed on the button.'),
        blank=True,
    )

    button_sv = models.TextField(
        verbose_name=_('Swedish button text'),
        help_text=_('Enter the text to be displayed on the button.'),
        blank=True,
    )

    button = TranslatedField('button_en', 'button_sv')

    # ------ Administrator settings ------
    panels = [
        MultiFieldPanel([
            ImageChooserPanel('image'),
            FieldRowPanel([
                FieldPanel('title_en'),
                FieldPanel('title_sv'),
            ]),
            FieldPanel('text_en'),
            FieldPanel('text_sv'),
            FieldPanel('link'),
            FieldRowPanel([
                FieldPanel('button_en'),
                FieldPanel('button_sv'),
            ]),
        ])
    ]
Exemple #13
0
class Author(models.Model):
    """
    The author snippet gives a way to relate authors to other content and create
    a range of relationships (e.g. one-to-one, one-to-many or many-to-many
    relationships) with content
    """

    search_fields = Page.search_fields + [
        # Defining what fields the search catches
        index.SearchField('title'),
        index.SearchField('biography'),
    ]

    title = models.CharField("The author's name", blank=True, max_length=254)

    slug = models.SlugField(
        allow_unicode=True,
        max_length=255,
        help_text=
        "The name of the page as it will appear in URLs e.g http://domain.com/blog/[my-slug]/",
    )

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

    job_title = models.CharField("The author's job title",
                                 blank=True,
                                 max_length=254,
                                 help_text="e.g. punk rock person")

    # Note below that standard blocks use 'help_text' for supplementary text
    # rather than 'label' as with StreamField
    biography = RichTextField(blank=True,
                              help_text="Short biography about the user")

    external_url = models.URLField(blank=True, null=True)

    @property
    def url(self):
        return '/authors/' + self.slug

    panels = [
        # The content panels are displaying the components of content we defined
        # in the StandardPage class above. If you add something to the class and
        # want it to appear for the editor you have to define it here too
        # A full list of the panel types you can use is at
        # http://docs.wagtail.io/en/latest/reference/pages/panels.html
        # If you add a different type of panel ensure you've imported it from
        # wagtail.wagtailadmin.edit_handlers in the `From` statements at the top
        # of the model
        MultiFieldPanel([
            FieldPanel('title'),
            FieldPanel('slug'),
        ],
                        heading="Title"),
        FieldPanel('job_title'),
        ImageChooserPanel('image'),
        FieldPanel('biography'),
        FieldPanel('external_url')
    ]

    def __str__(self):
        # We're returning the string that populates the snippets screen.
        # Note it returns a plain-text string. Reference the `artist_image`
        # below for returning a HTML rendition
        return self.title

    @property
    def image_listing(self):
        # fail silently if there is no profile pic or the rendition file can't
        # be found. Note @richbrennan worked out how to do this...
        try:
            return self.image.get_rendition('fill-150x150').img_tag()
        except:
            return ''

    @property
    def image_listing_small(self):
        # Needs to fail silently because image = null
        try:
            return self.image.get_rendition('fill-50x50').img_tag()
        except:
            return ''
Exemple #14
0
class ExpertPage(MobileTemplateMixin, RoutablePageMixin, Page):
    section = models.SmallIntegerField(verbose_name='Тема',
                                       db_index=True,
                                       default=SECTION_BEAUTY,
                                       choices=SECTION_CHOICES)

    user = models.OneToOneField(settings.AUTH_USER_MODEL,
                                verbose_name='Пользователь',
                                related_name='expert_page',
                                db_index=True,
                                on_delete=models.PROTECT,
                                limit_choices_to={'groups__name': "Эксперты"},
                                null=True,
                                blank=True)

    image = models.ForeignKey('content.CustomImage',
                              on_delete=models.PROTECT,
                              related_name='+',
                              verbose_name="Изображение",
                              null=True)
    intro = models.TextField(verbose_name='Интро',
                             max_length=500,
                             null=True,
                             blank=True)

    short_description = models.CharField(
        verbose_name='Регалии',
        max_length=150,
        null=True,
    )

    description = models.TextField(verbose_name='Описание',
                                   max_length=300,
                                   null=True,
                                   blank=True)

    old_id = models.IntegerField(null=True, blank=True)

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

    content_panels = Page.content_panels + [
        FieldPanel('section'),
        FieldPanel('user'),
        ImageChooserPanel('image'),
        FieldPanel('intro'),
        FieldPanel('description'),
        FieldPanel('short_description'),
    ]

    subpage_types = ['experts.AnswerPage']
    parent_page_types = ['experts.ExpertIndex']
    ajax_template = 'experts/expert_page_ajax.html'

    @route(r'^add_question/$', name='add_question')
    def add_question(self, request):
        from experts.forms import NewQuestionForm

        if request.method != 'POST' or not request.user.is_authenticated:
            raise Http404
        form = NewQuestionForm(request.POST, user=request.user)
        context = self.get_context(request)

        if form.is_valid():
            answer = form.save(commit=False)
            self.add_child(instance=answer)
            return redirect(self.get_url() + '?success=true#form')

        context['form'] = form
        return render(request, self.template, context)

    def get_context(self, request, *args, **kwargs):
        from experts.forms import NewQuestionForm

        context = super(ExpertPage, self).get_context(request, *args, **kwargs)
        answers = AnswerPage.objects.live()
        answers = answers.filter(published_at__lte=timezone.now(
        )).descendant_of(self).order_by('-published_at')
        context['form'] = NewQuestionForm(user=request.user)

        page = 1
        page = request.POST.get('page') or request.GET.get('page') or page
        if request.GET.get('q'):
            q = request.GET.get('q', '')
            answers = answers.search(q)
            context['q'] = q
        paginator = Paginator(answers, per_page=5)
        try:
            page_obj = paginator.page(page)
        except PageNotAnInteger:
            page_obj = paginator.page(1)
        except EmptyPage:
            page_obj = paginator.page(paginator.num_pages)
        context['answers'] = page_obj.object_list
        context['page_obj'] = page_obj

        if request.GET.get('success'):
            context['form_success'] = True

        return context

    def top_answers(self):
        return AnswerPage.objects.descendant_of(self)[:5]

    def serve(self, request, *args, **kwargs):
        page_models = (AnswerPage, )
        prefix = prefix_key('expert_page', page_models, 1,
                            request.user_agent.is_mobile)
        return cache_page(60 * 60,
                          key_prefix=prefix)(super(ExpertPage,
                                                   self).serve)(request, *args,
                                                                **kwargs)

    class Meta:
        verbose_name = "Страница эксперта"
        verbose_name_plural = "Страницы эксперта"
Exemple #15
0
class Person(ClusterableModel, index.Indexed):

    user = OneToOneField(User,
                         null=True,
                         blank=True,
                         on_delete=CASCADE,
                         related_name='profile')
    first_name = CharField(max_length=255)
    middle_name = CharField(max_length=255, null=True, blank=True)
    last_name = CharField(max_length=255)
    bio = RichTextField(blank=True)

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

    position = CharField(max_length=140, blank=True)
    term = CharField(blank=True, max_length=9, help_text="Format:YYYY-YYYY")
    linked_in = URLField(blank=True)
    blog_url = URLField(blank=True)
    osf_profile = URLField(blank=True)
    google_plus = URLField(blank=True)
    github = URLField(blank=True)
    twitter = URLField(blank=True)
    phone_number = CharField(max_length=12,
                             blank=True,
                             help_text="Format:XXX-XXX-XXXX")
    email_address = EmailField(blank=True)

    favorite_food = CharField(max_length=140, blank=True)

    tags = TaggableManager(through='common.PersonTag', blank=True)

    search_fields = [
        index.SearchField('first_name', partial_match=True),
        index.SearchField('last_name', partial_match=True),
        index.SearchField('middle_name', partial_match=True),
    ]

    panels = [
        MultiFieldPanel([
            FieldPanel('user'),
            FieldPanel('first_name'),
            FieldPanel('middle_name'),
            FieldPanel('last_name'),
            FieldPanel('bio'),
            FieldPanel('tags'),
            FieldPanel('position'),
            FieldPanel('term'),
            FieldPanel('linked_in'),
            FieldPanel('blog_url'),
            FieldPanel('osf_profile'),
            FieldPanel('google_plus'),
            FieldPanel('github'),
            FieldPanel('twitter'),
            FieldPanel('phone_number'),
            FieldPanel('email_address'),
            FieldPanel('favorite_food'),
        ],
                        heading='Basic Information'),
        ImageChooserPanel('photo'),
    ]

    class Meta:
        verbose_name_plural = "People"
        ordering = ['last_name']

    def __str__(self):
        return '{self.last_name}, {self.first_name}'.format(self=self)
Exemple #16
0
class BlogIndexPage(RoutablePageMixin, Page):
    """
    Index page for blogs.
    We need to alter the page model's context to return the child page objects,
    the BlogPage objects, so that it works as an index page

    RoutablePageMixin is used to allow for a custom sub-URL for the tag views
    defined above.
    """
    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.')

    content_panels = Page.content_panels + [
        FieldPanel('introduction', classname="full"),
        ImageChooserPanel('image'),
    ]

    # Speficies that only BlogPage objects can live under this index page
    subpage_types = ['BlogPage']

    # Defines a method to access the children of the page (e.g. BlogPage
    # objects). On the demo site we use this on the HomePage
    def children(self):
        return self.get_children().specific().live()

    # Overrides the context to list all child items, that are live, by the
    # date that they were published
    # http://docs.wagtail.io/en/latest/getting_started/tutorial.html#overriding-context
    def get_context(self, request):
        context = super(BlogIndexPage, self).get_context(request)
        context['posts'] = BlogPage.objects.descendant_of(
            self).live().order_by('-date_published')
        return context

    # This defines a Custom view that utilizes Tags. This view will return all
    # related BlogPages for a given Tag or redirect back to the BlogIndexPage.
    # More information on RoutablePages is at
    # http://docs.wagtail.io/en/latest/reference/contrib/routablepage.html
    @route('^tags/$', name='tag_archive')
    @route('^tags/(\w+)/$', name='tag_archive')
    def tag_archive(self, request, tag=None):

        try:
            tag = Tag.objects.get(slug=tag)
        except Tag.DoesNotExist:
            if tag:
                msg = 'There are no blog posts tagged with "{}"'.format(tag)
                messages.add_message(request, messages.INFO, msg)
            return redirect(self.url)

        posts = self.get_posts(tag=tag)
        context = {'tag': tag, 'posts': posts}
        return render(request, 'blog/blog_index_page.html', context)

    def serve_preview(self, request, mode_name):
        # Needed for previews to work
        return self.serve(request)

    # Returns the child BlogPage objects for this BlogPageIndex.
    # If a tag is used then it will filter the posts by tag.
    def get_posts(self, tag=None):
        posts = BlogPage.objects.live().descendant_of(self)
        if tag:
            posts = posts.filter(tags=tag)
        return posts

    # Returns the list of Tags for all child posts of this BlogPage.
    def get_child_tags(self):
        tags = []
        for post in self.get_posts():
            # Not tags.append() because we don't want a list of lists
            tags += post.get_tags
        tags = sorted(set(tags))
        return tags
Exemple #17
0
class Produce(models.Model):
    name = models.CharField(max_length=255)
    about = models.TextField(blank=True)

    category = models.ForeignKey(Category,
                                 null=True,
                                 blank=True,
                                 on_delete=models.PROTECT)

    is_fresh = models.BooleanField(
        default=True,
        verbose_name='Fresh',
        help_text=_('This is a fresh product (i.e. unprocessed).'))
    is_glutenfree = models.BooleanField(
        default=True,
        verbose_name='Gluten-free',
        help_text=_('Check if this product is free of gluten.'))
    is_dairyfree = models.BooleanField(
        default=True,
        verbose_name='Dairy-free',
        help_text=_('Milk is not part of this produce.'))
    is_nutsfree = models.BooleanField(
        default=True,
        verbose_name='Nut-free',
        help_text=_('Nuts are not part of this produce.'))
    is_vegan = models.BooleanField(
        default=True,
        verbose_name='Vegan',
        help_text=_('This is not an animal product.'))

    QUANTITYCHOICE = (
        ('kg', 'Kilogram'),
        ('g', 'Gram'),
        ('l', 'Litre'),
        ('b', 'Bushel'),
    )
    price_quantity = models.CharField(max_length=2,
                                      choices=QUANTITYCHOICE,
                                      null=True,
                                      blank=True)
    price_chf = MoneyField(max_digits=10,
                           decimal_places=2,
                           default_currency='CHF',
                           null=True,
                           blank=True)

    image = models.ForeignKey('wagtailimages.Image',
                              null=True,
                              blank=True,
                              on_delete=models.SET_NULL,
                              related_name='+')
    labels = models.ManyToManyField(
        Label,
        blank=True,
        help_text=_('What special modes of production were used.'))
    farms = models.ManyToManyField(
        Farm,
        blank=True,
        related_name='produce',
        help_text=_('Where is this produce available.'))

    panels = [
        FieldPanel('name'),
        FieldPanel('farms'),
        MultiFieldPanel(
            [
                FieldPanel('category'),
                FieldPanel('about'),
                ImageChooserPanel('image'),
                FieldPanel('price_chf'),
                FieldPanel('price_quantity'),
            ],
            heading="Details",
            classname="col5",
        ),
        MultiFieldPanel(
            [
                FieldPanel('is_fresh'),
                FieldPanel('is_glutenfree'),
                FieldPanel('is_dairyfree'),
                FieldPanel('is_nutsfree'),
                FieldPanel('is_vegan'),
                FieldPanel('labels'),
            ],
            heading="Features",
            classname="col7",
        ),
    ]

    api_fields = [
        APIField('name'),
        APIField('about'),
        APIField('category'),
        APIField('image_thumb',
                 serializer=ImageRenditionField('width-160', source='image')),
        APIField('image_full',
                 serializer=ImageRenditionField('width-800', source='image')),
        APIField('labels'),
        APIField('farms'),
    ]
    api_meta_fields = [
        'is_fresh',
        'is_glutenfree',
        'is_dairyfree',
        'is_nutsfree',
        'is_vegan',
    ]

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = 'Produce'
Exemple #18
0
class BlogPage(Page):
    """
    A Blog Page

    We access the People object with an inline panel that references the
    ParentalKey's related_name in BlogPeopleRelationship. More docs:
    http://docs.wagtail.io/en/latest/topics/pages.html#inline-models
    """
    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)
    subtitle = models.CharField(blank=True, max_length=255)
    tags = ClusterTaggableManager(through=BlogPageTag, blank=True)
    date_published = models.DateField("Date article published",
                                      blank=True,
                                      null=True)

    content_panels = Page.content_panels + [
        FieldPanel('subtitle', classname="full"),
        FieldPanel('introduction', classname="full"),
        ImageChooserPanel('image'),
        StreamFieldPanel('body'),
        FieldPanel('date_published'),
        InlinePanel('blog_person_relationship',
                    label="Author(s)",
                    panels=None,
                    min_num=1),
        FieldPanel('tags'),
    ]

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

    def authors(self):
        """
        Returns the BlogPage's related People. Again note that we are using
        the ParentalKey's related_name from the BlogPeopleRelationship model
        to access these objects. This allows us to access the People objects
        with a loop on the template. If we tried to access the blog_person_
        relationship directly we'd print `blog.BlogPeopleRelationship.None`
        """
        authors = [n.people for n in self.blog_person_relationship.all()]

        return authors

    @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

    # Specifies parent to BlogPage as being BlogIndexPages
    parent_page_types = ['BlogIndexPage']

    # Specifies what content types can exist as children of BlogPage.
    # Empty list means that no child content types are allowed.
    subpage_types = []
Exemple #19
0
class Person(models.Model, index.Indexed):
    first_name = models.CharField(max_length=255)
    last_name = models.CharField(max_length=255)
    role = models.CharField(max_length=255, blank=True)

    photo = models.ForeignKey('localore_admin.LocaloreImage',
                              null=True,
                              on_delete=models.SET_NULL,
                              related_name='+')

    production = models.ForeignKey(
        'productions.ProductionPage',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        verbose_name="associated production",
        help_text="Leave blank for Finding America and #LocaloreLive staff.")

    biography = RichTextField(blank=True)

    email = models.EmailField(blank=True)
    twitter_url = models.URLField("Twitter URL", blank=True)
    instagram_url = models.URLField("Instagram URL", blank=True)

    # Wagtail search
    search_fields = [
        index.SearchField('first_name'),
        index.SearchField('last_name'),
    ]

    # Wagtail admin
    panels = [
        MultiFieldPanel([
            FieldPanel('first_name'),
            FieldPanel('last_name'),
            ImageChooserPanel('photo'),
        ], "Name and Photo"),
        MultiFieldPanel([
            PageChooserPanel('production', 'productions.ProductionPage'),
            FieldPanel('role'),
        ], "Production"),
        FieldPanel('biography', classname="full"),
        MultiFieldPanel([
            FieldPanel('email'),
            FieldPanel('twitter_url'),
            FieldPanel('instagram_url'),
        ], "Contact")
    ]

    @property
    def full_name(self):
        return self.first_name + " " + self.last_name

    @property
    def role_and_production(self):
        if self.role and self.production:
            return self.role + ", " + self.production.title
        elif self.role:
            return self.role
        elif self.production:
            return self.production

    class Meta:
        ordering = ('last_name', )
        verbose_name = "Team Member"

    def __str__(self):
        out = [self.first_name, self.last_name]

        if self.role_and_production:
            out.append("(%s)" % self.role_and_production)

        return " ".join(out)
Exemple #20
0
class ArticlePage(Page):

    ARTICLE_TYPE = (
        ('N', 'News'),
        ('P', 'Press'),
    )

    ### Model Fields ######################################
    article_type = models.CharField(max_length=1,
                                    choices=ARTICLE_TYPE,
                                    default='N')

    subtitle = models.CharField(
        blank=True,
        null=True,
        max_length=200,
        help_text='Subtitle to be display at the banner. Maximum 200 words')

    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()), ('image_row', ImageGrid())],
        null=True,
    )

    publish_date = models.DateField("Date Article Published",
                                    default=datetime.today)

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

    tags = ClusterTaggableManager(through=ArticleTag, blank=True)

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

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

    ### Methods ###########################################
    def author_details(self):

        author_details = []
        n = self.article_author_relationship.first()
        if n:
            author_details = {
                'first_name': n.author.first_name,
                'last_name': n.author.last_name,
                'nick_name': n.author.nick_name,
                'profile_image_url': n.author.profile_image_url
            }

        return author_details

    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)

    def body_rendered(self):
        def replace_multi_nbsp_space(matchobj):
            # This function checks for any instance where there's
            # &nbsp followed by a space occurring MULTIPLE times.
            # This is because the rich text editor resolves multiple
            # continuous spaces as (  ). We return essentially the
            # same thing, but with 1 less entry.
            count = len(re.findall(r"(  )", matchobj.group(0)))
            return '&nbsp;' * (count - 1)

        def replace_multi_space(matchobj):
            # Turn multiple spaces into &nbsp; * (count - 1) so that
            # we can have words like S A D B O Y S, and it won't
            # break on new lines.
            count = len(re.findall(r"( )", matchobj.group(0)))
            return ('&nbsp;' * (count - 1))

        text = self.body.render_as_block()
        text = html_replace_img(text)
        text = re.sub(r"(  ){2,}", replace_multi_nbsp_space, text)
        text = re.sub(r"(?<=[^ ]) {1}(?=[^ ])", " ", text)
        text = re.sub(r"( ){2,}", replace_multi_space, text)
        return text

    ### CMS and API Exposure ##############################
    # Content panels - What shows up on the CMS dashboard
    content_panels = Page.content_panels + [
        FieldPanel('subtitle', classname="full"),
        FieldPanel('article_type'),
        FieldPanel('genre'),
        FieldPanel('tags'),
        FieldPanel('publish_date'),
        InlinePanel('article_author_relationship',
                    label="Author",
                    panels=None,
                    min_num=0,
                    max_num=1),
        MultiFieldPanel(
            [
                ImageChooserPanel('thumbnail'),
                ImageChooserPanel('banner'),
            ],
            heading="Article Main Images",
        ),
        StreamFieldPanel('body'),
    ]

    subpage_types = []
    parent_page_types = ['ArticleIndexPage']

    # API fields - What will be returned from API call
    api_fields = [
        APIField('subtitle'),
        APIField('article_type'),
        APIField('genre'),
        APIField('tags'),
        APIField('publish_date'),
        APIField('author_details'),
        APIField('thumbnail_url'),
        APIField('banner_url'),
        APIField('body_rendered'),
    ]

    # Context - used for 'Preview' template rendering
    def get_context(self, request):
        context = super(ArticlePage, self).get_context(request)

        # Default api fields
        # http://docs.wagtail.io/en/v1.9/reference/contrib/api/configuration.html
        context['title'] = self.title
        context['id'] = self.id

        # Article api fields
        context['article_type'] = self.article_type
        context['subtitle'] = self.subtitle
        context['body'] = self.body
        context['body_rendered'] = self.body_rendered
        context['publish_date'] = self.publish_date
        context['genre'] = self.genre
        context['author_details'] = self.author_details
        context['thumbnail_url'] = self.thumbnail_url
        context['banner_url'] = self.banner_url
        # context['tags'] = self.tags

        return context
Exemple #21
0
class Category(models.Model):
    name = models.CharField(max_length=255)
    name_es = models.CharField(max_length=255)
    slug = models.SlugField(help_text="Do not edit this field")
    slug_es = models.SlugField(help_text="Do not edit this field")
    intro = RichTextField(
        blank=True,
        help_text=(
            "Do not use H2, H3, H4, or H5 to style this text. "
            "Do not add links, images, videos or other rich text elements."))
    intro_es = RichTextField(
        blank=True,
        help_text=("Do not use this field. "
                   "It is not currently displayed on the front end."))
    category_image = models.ForeignKey(
        'v1.CFGOVImage',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text=('Select a custom image to appear when visitors share pages '
                   'belonging to this category on social media.'))
    panels = [
        FieldPanel('name', classname="title"),
        FieldPanel('slug'),
        FieldPanel('intro'),
        FieldPanel('name_es', classname="title"),
        FieldPanel('slug_es'),
        FieldPanel('intro_es'),
        ImageChooserPanel('category_image')
    ]

    def __str__(self):
        return self.name

    def featured_answers(self):
        return Answer.objects.filter(category=self,
                                     featured=True).order_by('featured_rank')

    @property
    def top_tags_es(self):
        import collections
        valid_dict = Answer.valid_tags(language='es')
        cleaned = []
        for a in self.answer_set.all():
            cleaned += a.clean_tags_es
        valid_clean = [
            tag for tag in cleaned if tag in valid_dict['valid_tags']
        ]
        counter = collections.Counter(valid_clean)
        return counter.most_common()[:10]

    @cached_property
    def facet_map(self):
        raw_answers = self.answer_set.order_by('-pk').select_related()
        answers = [
            answer for answer in raw_answers if answer.english_page.live
        ]
        subcats = self.subcategories.all().select_related()
        audiences = Audience.objects.all()
        container = {'subcategories': {}, 'audiences': {}}
        container['answers'] = OrderedDict([
            (str(answer.pk), {
                'question': answer.question,
                'url': '/ask-cfpb/slug-en-{}'.format(answer.pk)
            }) for answer in answers
            if answer.answer_pages.filter(language='en', redirect_to=None)
        ])
        subcat_data = {}
        for subcat in subcats:
            key = str(subcat.id)
            subcat_data[key] = [
                str(answer.pk) for answer in subcat.answer_set.all()
                if answer.answer_pages.filter(language='en', redirect_to=None)
            ]
        container['subcategories'].update(subcat_data)
        audience_map = {
            audience: {
                'all': [],
                'name': audience.name
            }
            for audience in audiences
        }
        for answer in answers:
            for audience in audience_map:
                if audience in answer.audiences.all():
                    audience_map[audience]['all'].append(str(answer.pk))
        for subcat in subcats:
            ID = str(subcat.id)
            for audience in audience_map:
                _map = audience_map[audience]
                if _map['all']:
                    _map[ID] = []
                    for answer_id in subcat_data[ID]:
                        if answer_id in _map['all']:
                            _map[ID].append(answer_id)
        container['audiences'].update({
            str(audience.id): audience_map[audience]
            for audience in audience_map.keys()
        })
        return json.dumps(container)

    class Meta:
        ordering = ['name']
        verbose_name_plural = 'Categories'
Exemple #22
0
class Article(Page):
    """Configurations for the various article pages"""

    # Model fields
    body = RichTextField()
    date = models.DateField("Post date")
    theme = models.ForeignKey(
        'months.Month',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='article_theme',
    )
    main_image = models.ForeignKey('wagtailimages.Image',
                                   null=True,
                                   blank=True,
                                   on_delete=models.SET_NULL,
                                   related_name='+')
    main_image_float = models.BooleanField(
        default=False,
        verbose_name='Float Image',
        help_text=
        "Select if the image should float vertically on the details page",
    )
    text_by = models.ForeignKey(
        'Contributor',
        null=False,
        blank=False,
        related_name='text_by',
        on_delete=models.PROTECT,
    )
    art_by = models.ForeignKey('Contributor',
                               null=True,
                               blank=True,
                               on_delete=models.SET_NULL,
                               related_name='art_by')

    # Search indexing
    search_fields = Page.search_fields + [
        index.SearchField('body'),
        index.FilterField('date'),
        index.RelatedFields('text_by', [
            index.SearchField('first_name'),
            index.SearchField('last_name'),
        ]),
        index.RelatedFields('art_by', [
            index.SearchField('first_name'),
            index.SearchField('last_name'),
        ]),
    ]

    # Panel configuration
    content_panels = Page.content_panels + [
        FieldPanel('date'),
        FieldPanel('theme'),
        MultiFieldPanel(
            [
                ImageChooserPanel('main_image'),
                FieldPanel('main_image_float'),
            ],
            heading="Image",
        ),
        MultiFieldPanel(
            [
                FieldPanel('text_by'),
                FieldPanel('art_by'),
            ],
            heading="Attribution",
            classname="collapsible collapsed",
        ),
        FieldPanel('body', classname="full"),
    ]

    objects = PageManager()

    parent_page_types = [
        'home.ArticleIndexPage',
    ]
    subpage_types = []

    def get_absolute_url(self):
        return self.url
Exemple #23
0
        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 = ['blog.BlogIndexPage']


BlogPage.content_panels = [
    FieldPanel('title', classname="full title"),
    MultiFieldPanel([
        FieldPanel('tags'),
        InlinePanel('categories', label=_("Categories")),
    ],
                    heading="Tags and Categories"),
    ImageChooserPanel('header_image'),
    StreamFieldPanel('body')
]
class ImageChooserPanelPage(WagtailPage):
    image = models.ForeignKey('wagtailimages.Image')

    content_panels = [
        ImageChooserPanel('image'),
    ]
Exemple #25
0
class DonationPage(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 DonationOperatingHours.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(DonationPage, 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 = ['DonationsIndexPage']
class ImageChooserPanelSnippet(models.Model):
    image = models.ForeignKey('wagtailimages.Image')

    panels = [
        ImageChooserPanel('image'),
    ]
Exemple #27
0
class HomePage(Page):
    YEAR_CHOICES = (
        ('2017', '2017'),
        ('2018', '2018'),
        ('2019', '2019'),
    )

    MONTH_CHOICES = (
        ('1', '1'),
        ('2', '2'),
        ('3', '3'),
        ('4', '4'),
        ('5', '5'),
        ('6', '6'),
        ('7', '7'),
        ('8', '8'),
        ('9', '9'),
        ('10', '10'),
        ('11', '11'),
        ('12', '12'),
    )

    DAY_CHOICES = (
        ('1', '1'),
        ('2', '2'),
        ('3', '3'),
        ('4', '4'),
        ('5', '5'),
        ('6', '6'),
        ('7', '7'),
        ('8', '8'),
        ('9', '9'),
        ('10', '10'),
        ('11', '11'),
        ('12', '12'),
        ('13', '13'),
        ('14', '14'),
        ('15', '15'),
        ('16', '16'),
        ('17', '17'),
        ('18', '18'),
        ('19', '19'),
        ('20', '20'),
        ('21', '21'),
        ('22', '22'),
        ('23', '23'),
        ('24', '24'),
        ('25', '25'),
        ('26', '26'),
        ('27', '27'),
        ('28', '28'),
        ('29', '29'),
        ('30', '30'),
        ('31', '31'),
    )
    HOUR_CHOICES = (
        ('1', '1'),
        ('2', '2'),
        ('3', '3'),
        ('4', '4'),
        ('5', '5'),
        ('6', '6'),
        ('7', '7'),
        ('8', '8'),
        ('9', '9'),
        ('10', '10'),
        ('11', '11'),
        ('12', '12'),
        ('13', '13'),
        ('14', '14'),
        ('15', '15'),
        ('16', '16'),
        ('17', '17'),
        ('18', '18'),
        ('19', '19'),
        ('20', '20'),
        ('21', '21'),
        ('22', '22'),
        ('23', '23'),
        ('24', '24'),
    )
    MINSEC_CHOICES = (
        ('0', '0'),
        ('1', '1'),
        ('2', '2'),
        ('3', '3'),
        ('4', '4'),
        ('5', '5'),
        ('6', '6'),
        ('7', '7'),
        ('8', '8'),
        ('9', '9'),
        ('10', '10'),
        ('11', '11'),
        ('12', '12'),
        ('13', '13'),
        ('14', '14'),
        ('15', '15'),
        ('16', '16'),
        ('17', '17'),
        ('18', '18'),
        ('19', '19'),
        ('20', '20'),
        ('21', '21'),
        ('22', '22'),
        ('23', '23'),
        ('24', '24'),
        ('25', '25'),
        ('26', '26'),
        ('27', '27'),
        ('28', '28'),
        ('29', '29'),
        ('30', '30'),
        ('31', '31'),
        ('32', '32'),
        ('33', '33'),
        ('34', '34'),
        ('35', '35'),
        ('36', '36'),
        ('37', '37'),
        ('38', '38'),
        ('39', '39'),
        ('40', '40'),
        ('41', '41'),
        ('42', '42'),
        ('43', '43'),
        ('44', '44'),
        ('45', '45'),
        ('46', '46'),
        ('47', '47'),
        ('48', '48'),
        ('49', '49'),
        ('50', '50'),
        ('51', '51'),
        ('52', '52'),
        ('53', '53'),
        ('54', '54'),
        ('55', '55'),
        ('56', '56'),
        ('57', '57'),
        ('58', '58'),
        ('59', '59'),
    )

    background = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+'
    )
    couplename = models.CharField(max_length=255, default="Someone & Someone")
    subtitle = models.CharField(max_length=255, blank=True, default="We are getting married")
    year = models.CharField(max_length=4, choices=YEAR_CHOICES, default="2017")
    month = models.CharField(max_length=2, choices=MONTH_CHOICES, default="7")
    day = models.CharField(max_length=2, choices=DAY_CHOICES, default="1")
    hour = models.CharField(max_length=2, choices=HOUR_CHOICES, default="12")
    minute = models.CharField(max_length=2, choices=MINSEC_CHOICES, default="0")
    seconds = models.CharField(max_length=2, choices=MINSEC_CHOICES, default="0")
    icsfile = models.ForeignKey(
        'wagtaildocs.Document',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+'
    )

    body = StreamField(
        [('CoupleBlock', CoupleBlock()),
         ('TimeLineBlock', TimeLineBlock()),
         ], default="")

    content_panels = Page.content_panels + [
        FieldPanel('couplename', classname="full"),
        FieldPanel('subtitle', classname="full"),
        FieldRowPanel([
            FieldPanel('year'),
            FieldPanel('month'),
            FieldPanel('day'),
            FieldPanel('hour'),
            FieldPanel('minute'),
            FieldPanel('seconds')
        ]),
        ImageChooserPanel('background'),
        DocumentChooserPanel('icsfile'),
        StreamFieldPanel('body'),
    ]
Exemple #28
0
    feed_image = models.ForeignKey('core.OvercastImage',
                                   null=True,
                                   blank=True,
                                   on_delete=models.SET_NULL,
                                   related_name='+')

    indexed_fields = ('title', )
    search_name = None

    class Meta:
        abstract = True


BASESTANDARDPAGE_CONTENT_PANELS = [
    FieldPanel('title', classname="full title"),
    ImageChooserPanel('header_image'),
    InlinePanel('content_block', label="Content block"),
    InlinePanel('related_links', label="Related links"),
]

BASESTANDARDPAGE_PROMOTE_PANELS = [
    MultiFieldPanel(COMMON_PANELS, "Common page configuration"),
    ImageChooserPanel('feed_image'),
]


class StandardPage(BaseStandardPage):
    streamfield = StreamField(StoryBlock())


StandardPage.content_panels = [
        on_delete=models.SET_NULL,
        related_name='+'
    )

    indexed_fields = ('intro', )

StandardIndexPage.content_panels = [
    FieldPanel('title', classname="full title"),
    FieldPanel('subtitle', classname="full title"),
    FieldPanel('intro', classname="full"),
    InlinePanel(StandardIndexPage, 'related_links', label="Related links"),
]

StandardIndexPage.promote_panels = [
    MultiFieldPanel(Page.promote_panels, "Common page configuration"),
    ImageChooserPanel('feed_image'),
]


# Standard page

class StandardPageCarouselItem(Orderable, CarouselItem):
    page = ParentalKey('pages.StandardPage', related_name='carousel_items')


class StandardPageRelatedLink(Orderable, RelatedLink):
    page = ParentalKey('pages.StandardPage', related_name='related_links')


class StandardPage(Page):
    subtitle = models.CharField(max_length=255, blank=True)
Exemple #30
0
class ProductPage(Page):
    author = models.ForeignKey('auth.User', on_delete=models.PROTECT)
    article_num = models.CharField(max_length=20)
    feature_image = models.ForeignKey('wagtailimages.Image',
                                      null=True,
                                      blank=True,
                                      on_delete=models.SET_NULL,
                                      related_name='+')
    thumbnail_image = models.ForeignKey('wagtailimages.Image',
                                        null=True,
                                        blank=True,
                                        on_delete=models.SET_NULL,
                                        related_name='+')
    short_desc = models.CharField(max_length=250, blank=True)
    #BrandSegment
    CON = 'Consumer'
    COM = 'Commercial'
    busigroup = (
        (CON, 'Consumer'),
        (COM, 'Commercial'),
    )
    busigroup_choice = models.CharField(
        max_length=200,
        choices=busigroup,
        default=CON,
    )
    #Brand
    brandtype = models.ForeignKey('product.Brand', on_delete=models.PROTECT)
    Live = 'Live'
    CS = 'Coming Soon'
    EOL = 'End of Life'
    status_type = (
        (Live, 'Live'),
        (CS, 'Coming Soon'),
        (EOL, 'End of Life'),
    )
    status_choice = models.CharField(
        max_length=200,
        choices=status_type,
        default=Live,
    )
    #Device Type
    LT = 'Laptops'
    DT = 'Desktops'
    DTA = 'Desktops - AIO'
    ACC = 'Accessories'
    WS = 'WorkStations'
    TB = 'Tablets'
    SD = 'Smart Devices'
    SSN = 'Servers Storage and Networking'
    device_type = (
        (LT, 'Laptops'),
        (DT, 'Desktops'),
        (DTA, 'Desktops All-In-Ones'),
        (ACC, 'Accessories'),
        (WS, 'Workstations'),
        (TB, 'Tablets'),
        (SD, 'Smart Devices'),
        (SSN, 'Servers, Storage and Networking'),
    )
    device_choice = models.CharField(
        max_length=200,
        choices=device_type,
        default=LT,
    )
    hybrislink = models.TextField()
    halo = models.BooleanField(default=False)
    touch = models.BooleanField(default=False)
    conv = models.BooleanField(default=False)
    producttag = TaggableManager()
    launch_notes = RichTextField(blank=True)
    created_date = models.DateTimeField(default=timezone.now)
    live_date = models.DateTimeField(blank=True, null=True)

    content_panels = Page.content_panels + [
        FieldPanel('author'),
        FieldPanel('article_num'),
        FieldPanel('busigroup_choice'),
        FieldPanel('brandtype'),
        FieldPanel('status_choice'),
        FieldPanel('device_choice'),
        FieldPanel('hybrislink'),
        FieldPanel('halo'),
        FieldPanel('touch'),
        FieldPanel('conv'),
        FieldPanel('producttag'),
        FieldPanel('created_date'),
        FieldPanel('live_date'),
        FieldPanel('launch_notes', classname="full"),
        ImageChooserPanel('feature_image'),
        ImageChooserPanel('thumbnail_image'),
    ]