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
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') ]
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'), ]) ]
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')
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) ]
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
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)
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 = []
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)
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'), ]
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
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'), ]), ]) ]
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 ''
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 = "Страницы эксперта"
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)
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
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'
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 = []
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)
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 #   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 ' ' * (count - 1) def replace_multi_space(matchobj): # Turn multiple spaces into * (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 (' ' * (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
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'
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
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'), ]
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'), ]
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'), ]
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)
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'), ]