class PortfolioPageJobCareer(GraphQLEnabledModel, Orderable): portfolio = ParentalKey(PortfolioPage, on_delete=models.CASCADE, related_name='job_career', verbose_name=u'職務経歴') title = models.CharField(verbose_name=u'タイトル', max_length=50) start_date = models.DateField(u'開始日', auto_now=False, auto_now_add=False) end_date = models.DateField(u'終了日', auto_now=False, auto_now_add=False, blank=True, null=True) job_role = models.CharField(u'役割', max_length=50, blank=True, null=True) description = MarkdownField(verbose_name=u'説明', blank=True, null=True) panels = [ FieldPanel('title'), FieldPanel('start_date'), FieldPanel('end_date'), FieldPanel('job_role'), MarkdownPanel('description') ] graphql_fields = [ GraphQLField('title'), GraphQLField('start_date'), GraphQLField('end_date'), GraphQLField('job_role'), GraphQLField('description'), ]
class AboutPage(RoutablePageMixin, Page): body = MarkdownField(null=True, blank=True) content_panels = Page.content_panels + [ MarkdownPanel('body', classname="full"), ] def get_context(self, request, *args, **kwargs): context = super().get_context(request, *args, **kwargs) context["posts"] = IndexDetailPage.objects.live().public() json_list = list(context["posts"].values( 'slug', 'title', 'author_founder', 'rownum', 'pub_date', 'end_date', 'about', 'location', 'external_link', 'external_link_two', 'images_list', 'page_ptr_id')) context['json_dict'] = json.dumps(json_list) context["image_entries"] = [] for index in context["posts"]: for c in index.images_list.all(): context["image_entries"].append({ "slug": index.slug, "img_name": str(c) }) context['json_img_dict'] = json.dumps(list(context["image_entries"])) return context @route(r'^submit/$', name="submit_page") def submit_page(self, request): return TemplateResponse(request, 'about/about_submit_page.html')
class StaticContent(models.Model): text = MarkdownField() panels = [ MarkdownPanel('text'), ] def __str__(self): return self.text
class PostPage(Page): body = MarkdownField() date = models.DateTimeField(verbose_name="Post date", default=datetime.datetime.today) excerpt = MarkdownField( verbose_name='excerpt', blank=True, ) header_image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', ) categories = ParentalManyToManyField('blog.BlogCategory', blank=True) tags = ClusterTaggableManager(through='blog.BlogPageTag', blank=True) content_panels = Page.content_panels + [ ImageChooserPanel('header_image'), MarkdownPanel("body"), MarkdownPanel("excerpt"), FieldPanel('categories', widget=forms.CheckboxSelectMultiple), FieldPanel('tags'), ] settings_panels = Page.settings_panels + [ FieldPanel('date'), ] @property def blog_page(self): return self.get_parent().specific def get_context(self, request, *args, **kwargs): context = super(PostPage, self).get_context(request, *args, **kwargs) context['blog_page'] = self.blog_page context['post'] = self return context
class PuzzlePostPage(Page): date = models.DateField('Post date') body = MarkdownField(blank=True) answer = MarkdownField(blank=True) def main_image(self): gallery_item = self.gallery_images.first() if gallery_item: return gallery_item.image else: return None search_fields = Page.search_fields + [ index.SearchField('body'), index.SearchField('answer'), ] content_panels = Page.content_panels + [ FieldPanel('date'), MarkdownPanel('body'), MarkdownPanel('answer'), InlinePanel('gallery_images', label='Gallery images'), ]
class SitePolicyPage(GraphQLEnabledModel, Page): body = MarkdownField(verbose_name=u'本文', blank=True) content_panels = Page.content_panels + [ MarkdownPanel("body", classname="full"), ] promote_panels = [ MultiFieldPanel(Page.promote_panels, "Common page configuration"), ] graphql_fields = [ GraphQLField('body'), ]
class CharacterPage(GraphQLEnabledModel, Page): """A page of character list.""" nickname = models.CharField(u"称号", max_length=15, null=True) name = models.CharField(u"名前", max_length=35, null=True) character_id = models.CharField(u"キャラクターID", max_length=6, null=True) image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', verbose_name=u'画像', ) description = models.CharField(u'概要', max_length=255, null=True) introduction = MarkdownField(verbose_name=u"説明", null=True) game_name = models.CharField(u"登録されているゲーム", max_length=20, null=True) character_page_url = models.CharField(u"キャラクターのページ", max_length=255, null=True) content_panels = Page.content_panels + [ FieldPanel('nickname'), FieldPanel('name'), FieldPanel('character_id'), ImageChooserPanel('image'), FieldPanel('description'), MarkdownPanel('introduction'), FieldPanel('game_name'), FieldPanel('character_page_url'), ] promote_panels = [ MultiFieldPanel(Page.promote_panels, "Common page configuration"), ] graphql_fields = [ GraphQLField("nickname"), GraphQLField("name"), GraphQLField("character_id"), GraphQLField("image"), GraphQLField("description"), GraphQLField("introduction"), GraphQLField("game_name"), GraphQLField("character_page_url"), ] def clean(self): super().clean() new_title = '%s(%s)' % (self.name, self.character_id) new_slug = '%s' % self.character_id self.title = new_title self.slug = slugify(new_slug)
class BookIndexPage(GraphQLEnabledModel, Page): intro = MarkdownField(null=True) def child_pages(self): return BookPage.objects.live().child_of(self) content_panels = Page.content_panels + [ MarkdownPanel('intro', classname='full') ] graphql_fields = [ GraphQLField('intro'), ] subpage_types = ['BookPage']
class BlogIndexPage(MetadataPageMixin, Page): sub_title = models.CharField(max_length=600, blank=True) body = MarkdownField(blank=True) image = models.ForeignKey('wagtailimages.Image', blank=True, null=True, on_delete=models.PROTECT, related_name='blog_index_image') content_panels = Page.content_panels + [ FieldPanel("sub_title", classname="blog index title"), MarkdownPanel("body"), FieldPanel('image', classname="blog index image"), ] search_fields = Page.search_fields + [ index.SearchField('sub_title'), index.SearchField('body'), ]
class ArticlePage(GraphQLEnabledModel, Page): """Article Pages""" date = models.DateTimeField(u"投稿日") tags = ClusterTaggableManager(verbose_name=u'タグ', through=ArticlePageTag, blank=True) body = MarkdownField(verbose_name=u'本文', blank=True) feed_image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', verbose_name=u'画像', ) author = models.ForeignKey( 'author.AuthorPage', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', verbose_name=u'著者', ) content_panels = Page.content_panels + [ FieldPanel('date'), FieldPanel('tags'), MarkdownPanel("body", classname="full"), ImageChooserPanel('feed_image'), PageChooserPanel('author', 'author.AuthorPage'), InlinePanel('related_links', label=u'関連リンク'), ] promote_panels = [ MultiFieldPanel(Page.promote_panels, "Common page configuration"), ] graphql_fields = [ GraphQLField('author'), GraphQLField('date'), GraphQLField('tags'), GraphQLField('slug'), GraphQLField('body'), GraphQLField('feed_image'), GraphQLField('related_links') ]
class HomePage(Page): home_title = models.CharField(max_length=600, blank=True) body = MarkdownField(blank=True) image = models.ForeignKey('wagtailimages.Image', blank=True, null=True, on_delete=models.PROTECT, related_name='home_image') content_panels = Page.content_panels + [ FieldPanel("home_title", classname="Home title"), MarkdownPanel("body"), FieldPanel('image', classname="Home image"), ] search_fields = Page.search_fields + [ index.SearchField('title'), index.SearchField('body'), ]
class BlogPage(MetadataPageMixin, Page): sub_title = models.CharField(max_length=600, blank=True) body = MarkdownField(blank=True) image = models.ForeignKey('wagtailimages.Image', blank=True, null=True, on_delete=models.PROTECT, related_name='blog_image') content_panels = Page.content_panels + [ FieldPanel("sub_title", classname="blog title"), MarkdownPanel("body"), FieldPanel('image', classname="blog image"), ] search_fields = Page.search_fields + [ index.SearchField('sub_title'), index.SearchField('body'), ] def get_context(self, request): context = super(BlogPage, self).get_context(request) context['recommended'] = BlogPage.objects.live().exclude(id=self.id).order_by('?')[:5] context['surah'] = Chapter.objects.order_by('?')[:10] return context
class BookPage(GraphQLEnabledModel, Page): price = models.IntegerField() published_date = models.DateField() published_event = models.CharField(max_length=50) description = MarkdownField(verbose_name=u'説明') booth_url = models.URLField(max_length=255, null=True) bookwalker_url = models.URLField(max_length=255, null=True) image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', ) content_panels = Page.content_panels + [ FieldPanel('price'), FieldPanel('published_date'), FieldPanel('published_event'), MarkdownPanel('description'), FieldPanel('booth_url'), FieldPanel('bookwalker_url'), ImageChooserPanel('image'), ] promote_panels = [ MultiFieldPanel(Page.promote_panels, "Common page configuration"), ] graphql_fields = [ GraphQLField('price'), GraphQLField('published_date'), GraphQLField('published_event'), GraphQLField('description'), GraphQLField('booth_url'), GraphQLField('bookwalker_url'), GraphQLField('image'), GraphQLField('slug') ]
class BaseResumePage(MetadataMixin, Page): page_ptr = models.OneToOneField(Page, parent_link=True, related_name="+", on_delete=models.CASCADE) is_creatable = False font = models.CharField(max_length=100, null=True, blank=True) background_color = models.CharField(max_length=100, null=True, blank=True) full_name = models.CharField(max_length=100, null=True, blank=True) role = models.CharField(max_length=100, null=True, blank=True) about = MarkdownField(max_length=2500, null=True, blank=True) photo = models.ForeignKey(Image, null=True, blank=True, on_delete=models.SET_NULL, related_name="+") social_links = fields.StreamField( [ ( "social_link", blocks.StructBlock( [ ("text", blocks.TextBlock()), ("url", blocks.URLBlock()), ("logo", ImageChooserBlock()), ], icon="group", ), ), ], null=True, blank=True, ) resume = fields.StreamField( [ ("work_experience", WorkExperienceBlock()), ("contributions", ContributionsBlock()), ("writing", WritingsBlock()), ("education", EducationBlock()), ], null=True, blank=True, ) content_panels = Page.content_panels + [ MultiFieldPanel( [ FieldPanel("font"), FieldPanel("background_color"), ], heading="Customization", ), MultiFieldPanel( [ FieldPanel("full_name"), FieldPanel("role"), MarkdownPanel("about"), ImageChooserPanel("photo"), StreamFieldPanel("social_links"), ], heading="Personal details", ), StreamFieldPanel("resume"), ] def get_template(self, request): return "wagtail_resume/resume_page.html" def get_meta_title(self): return self.full_name def get_meta_description(self): return f"{self.full_name} - {self.role}" def get_meta_image(self): return self.photo def get_meta_url(self): return self.get_full_url def get_meta_twitter_card_type(self): return self.photo
class IndexDetailPage(Page): about = MarkdownField(null=True, blank=True) sourceforabouttext = models.CharField("Source for about text", max_length=255, null=True, blank=True) categories = ParentalManyToManyField("index.IndexCategory", blank=True) pub_date = models.PositiveSmallIntegerField("Date Published / Created", null=True, blank=True) end_date = models.PositiveSmallIntegerField("End Date", null=True, blank=True) author_founder = models.CharField("Author/Founder", max_length=500, null=True, blank=True) contributed_by = models.CharField("Contributed By", max_length=500, null=True, blank=True) external_link = models.URLField(null=True, blank=True) external_link_two = models.URLField(null=True, blank=True) autoincrement_num = models.PositiveSmallIntegerField(null=True, blank=True) rownum = models.PositiveSmallIntegerField(null=True, blank=True) location = models.CharField("location", max_length=255, null=True, blank=True) # slug = models.SlugField(verbose_name=_('slug'), allow_unicode=True, max_length=255) search_fields = Page.search_fields + [ index.SearchField('title'), index.SearchField('author_founder'), index.SearchField('about'), index.SearchField('contributed_by'), index.SearchField('location'), index.SearchField('pub_date'), index.SearchField('end_date'), ] content_panels = Page.content_panels + [ MarkdownPanel('about', classname="full"), MultiFieldPanel([ FieldRowPanel([ FieldPanel('pub_date'), FieldPanel('end_date'), ]), FieldRowPanel([ FieldPanel('author_founder'), FieldPanel('location'), ]), FieldRowPanel([ FieldPanel('external_link'), FieldPanel('external_link_two'), ]), FieldRowPanel([ FieldPanel('contributed_by'), ]), ], 'Details'), MultiFieldPanel( [ InlinePanel('images_list', label='Image'), ], heading="Image(s)", ), MultiFieldPanel( [FieldPanel("categories", widget=forms.CheckboxSelectMultiple)], heading="Categories"), MultiFieldPanel( [ InlinePanel('collections_list', label='Curator'), ], heading="Curator(s)", ), ] promote_panels = [] class Meta: # noqa verbose_name = "Index Detail Page" verbose_name_plural = "Index Detail Pages"
class BlogPage(Page): """ This is the core of the Blog app. BlogPage are individual articles """ subtitle = models.CharField(blank=True, max_length=255) body = MarkdownField(blank=True) extended_body = StreamField([ ('content', MarkdownBlock(template='blog/markdown_block.html')), ('newsletter', NewsletterSubscribe()), ('book', BookInline()), ], null=True) image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', help_text='Header Image, used also for social sharing') image_data = RichTextField( blank=True, null=True, help_text= 'Information about the header image, to appear after the article') tags = ClusterTaggableManager(through=BlogPageTag, blank=True) date_published = models.DateField("Date article published", blank=True, null=True) allow_comments = models.BooleanField(default=True) content_panels = Page.content_panels + [ FieldPanel('subtitle'), StreamFieldPanel('extended_body'), MarkdownPanel('body'), ImageChooserPanel('image'), FieldPanel('image_data'), FieldPanel('date_published'), InlinePanel('blog_person_relationship', label="Author(s)", panels=None, min_num=1), FieldPanel('tags'), ] promote_panels = Page.promote_panels + [ FieldPanel('allow_comments'), ] 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 first_author(self): return self.authors()[-1] @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 = [] def get_absolute_url(self): return self.specific.url def get_context(self, request): context = super(BlogPage, self).get_context(request) context['latest_articles'] = BlogPage.objects.live().order_by( '-date_published')[:5] context['intro_class'] = 'blog article' return context @property def introduction(self): html = render_markdown(self.body) soup = BeautifulSoup(html, "html.parser") try: introduction = soup.find('p').text return introduction except AttributeError: return None class Meta: ordering = ['-date_published']
class Action(models.Model): name = models.CharField(max_length=255, db_index=True) when = models.DateTimeField(db_index=True) description = MarkdownField(default="", blank=True, help_text="Markdown formatted") slug = models.SlugField(unique=True, help_text="Short form of the title, for URLs") public = models.BooleanField( default=True, blank=True, help_text="Whether this action should be listed publicly", ) location = models.TextField( default="", blank=True, help_text="Event location will be converted to a google maps link, unless you format it as a Markdown link -- [link text](http://example.com)", ) virtual = models.BooleanField( default=False, help_text="Check this if the event is online (virtual zoom or other video conferencing)", ) available_roles = models.CharField( default="", blank=True, max_length=255, help_text="List of comma-separated strings", ) # Will need to figure out how to migrate this over. photos = models.ManyToManyField(Photo, blank=True) image = models.ForeignKey( "vaquita.CustomImage", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", ) modified = models.DateTimeField(auto_now=True) show_commitment = models.BooleanField( blank=True, default=False, help_text="Whether to show the conditional commitment fields", ) max_participants = models.IntegerField( blank=True, default=0, help_text="Maximun number of people allowed to register" ) accessibility = models.TextField( default="", help_text="Indicate what the accessibility accomodations are for this location.", ) contact_email = models.TextField( default="", null=True, help_text="Contact info of event organizer." ) tags = TaggableManager( blank=True, help_text="Attendees will automatically be tagged with these tags" ) objects = ActionManager() panels = [ MultiFieldPanel( [ FieldPanel("name", widget=forms.TextInput(attrs={"id": "id_title"})), FieldPanel("when"), FieldPanel("virtual"), FieldPanel("location"), FieldPanel("slug"), ] ), FieldPanel("contact_email"), MarkdownPanel("description", widget=ZOrderMarkdownTextarea), ImageChooserPanel("image"), FieldPanel("tags"), FieldRowPanel( [ FieldPanel("public"), FieldPanel("max_participants"), FieldPanel("show_commitment"), ] ), FieldPanel("accessibility"), ] @property def available_role_choices(self): for role in self.available_roles.split(","): role = role.strip() if role: yield role def is_full(self): return ( self.max_participants and self.attendee_set.count() >= self.max_participants ) def get_absolute_url(self): return reverse("extinctionr.actions:action", kwargs={"slug": self.slug}) @property def full_url(self): return f"{base_url()}{self.get_absolute_url()}" def signup(self, email, role, name="", notes="", promised=None, commit=0): if not isinstance(role, ActionRole): role = ActionRole.objects.get_or_create(name=role or "")[0] user = get_contact(email, name=name) atten, created = Attendee.objects.get_or_create( action=self, contact=user, role=role ) if not created: if notes: atten.notes = notes else: atten.notes = notes atten.mutual_commitment = commit if promised: atten.promised = now() atten.save() return atten @property def html_title(self): return mark_safe(self.name.replace("\n", "<br>").replace("\\n", "<br>")) @property def text_title(self): return self.name.replace("\\n", " ") def __str__(self): return f"{self.name} on {self.when.strftime('%b %e, %Y @ %H:%M')}" def save(self, *args, **kwargs): ret = super().save(*args, **kwargs) for role in self.available_role_choices: ActionRole.objects.get_or_create(name=role) return ret @property def location_link(self): if self.location.startswith("["): link = markdown(self.location) else: if self.virtual: link_text = "Online Meeting" else: link_text = self.location # See if the text is already a valid url. url_validator = URLValidator(schemes=["http", "https"]) try: url_validator(self.location) link = f"<a href={self.location}>{link_text}</a>" except ValidationError: if self.virtual: link = "/" # validation should have seen to this. else: link = '<a href="https://maps.google.com/?q={}">{}</a>'.format( quote(self.location), linebreaks(self.location) ) pass return mark_safe(link) @property def card_thumbnail_url(self): if self.photos: photo = self.photos.first() if photo: return photo.photo.url return None
class AuthorPage(GraphQLEnabledModel, Page): profile = MarkdownField(verbose_name=u'プロフィール') nickname = models.CharField(verbose_name=u'ニックネーム', max_length=25, null=True, blank=True) first_name = models.CharField(verbose_name=u'名', max_length=10, null=True, blank=True) middle_name = models.CharField(verbose_name=u'ミドルネーム', max_length=10, null=True, blank=True) family_name = models.CharField(verbose_name=u'姓', max_length=10, null=True, blank=True) name = models.CharField(verbose_name=u'表示名', max_length=80, null=True, blank=True) is_surname_first = models.BooleanField(verbose_name=u'姓が先の表記', null=True, blank=True) use_nickname = models.BooleanField(verbose_name=u'ニックネームの使用', null=True, blank=True) portrait = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', verbose_name=u'画像', ) interest = ClusterTaggableManager(verbose_name=u'興味を持っていること', through=AuthorPageInterest, blank=True) content_panels = Page.content_panels + [ MarkdownPanel('profile'), ImageChooserPanel('portrait'), FieldPanel('interest'), FieldPanel('nickname'), FieldPanel('first_name'), FieldPanel('middle_name'), FieldPanel('family_name'), FieldPanel('name'), FieldPanel('is_surname_first'), FieldPanel('use_nickname'), InlinePanel('portfolio_links', label=u'ポートフォリオ'), InlinePanel('amazon_wish_list_links', label=u'Amazonのほしいものリスト'), InlinePanel('sns_links', label=u'SNSなどのリンク'), ] promote_panels = [ MultiFieldPanel(Page.promote_panels, "Common page configuration"), ] graphql_fields = [ GraphQLField('name'), GraphQLField('profile'), GraphQLField('portrait'), GraphQLField('interest'), GraphQLField('portfolio_links'), GraphQLField('amazon_wish_list_links'), GraphQLField('sns_links'), ] def clean(self): """Rename title when posted.""" if self.nickname is not None and self.use_nickname is True: self.name = self.nickname elif self.is_surname_first is True: if self.middle_name is None: self.name = self.family_name + u' ' + self.first_name else: self.name = (self.family_name + u' ' + self.middle_name + u' ' + self.first_name) else: if self.middle_name is None: self.name = self.first_name + u' ' + self.family_name else: self.name = (self.first_name + u' ' + self.middle_name + u' ' + self.family_name) self.title = '%s のプロフィール' % self.name