示例#1
0
class Performance(Page):
    """
    A Performance page is a child of Concert pages.
    Its used to model performances of individual works within a Concert.
    These will publish as a list on the parent concert page.
    """
    base_form_class = PerformanceAdminForm
    composition = models.ForeignKey('Composition',
                                    null=False,
                                    blank=False,
                                    on_delete=models.PROTECT,
                                    related_name='+')
    supplemental_text = RichTextField(blank=True, features=['bold', 'italic'])
    conductor = models.ForeignKey('Person',
                                  null=True,
                                  blank=True,
                                  on_delete=models.PROTECT,
                                  related_name='+')
    performance_date = ParentalManyToManyField('ConcertDate', blank=True)

    def full_clean(self, *args, **kwargs):
        self.title = str(self.composition)
        # Clear sluge and let Wagtail resolve it
        self.slug = ''
        super().full_clean(*args, **kwargs)

    def clean(self):
        self.title = str(self.composition)
        super().clean()

    # Route requests to these pages to their parent
    def get_url_parts(self, *args, **kwargs):
        return self.get_parent().specific.get_url_parts(*args, **kwargs)

    content_panels = [
        AutocompletePanel('composition', target_model='main.Composition'),
        FieldPanel('supplemental_text'),
        InlinePanel('performer', label='Performers'),
        AutocompletePanel('conductor', target_model='main.Person'),
        FieldPanel(
            'performance_date',
            widget=forms.CheckboxSelectMultiple,
        )
    ]

    promote_panels = []

    parent_page_types = ['Concert']
    subpage_types = []
 def test_target_models_nonexistent_type(self):
     autocomplete_panel = AutocompletePanel(
         'owner',
         target_model='testapp.hous',
     ).bind_to(model=House)
     with self.assertRaises(ImproperlyConfigured):
         autocomplete_panel.target_model
    def test_render_multiple_as_field(self):
        edit_handler = (ObjectList([AutocompletePanel('occupants')
                                    ]).bind_to(model=House,
                                               request=self.request))
        form_class = edit_handler.get_form_class()
        form = form_class(instance=self.test_house)
        autocomplete_panel = edit_handler.children[0].bind_to(
            instance=self.test_house, form=form, request=self.request)

        result = autocomplete_panel.render_as_field()
        self.assertIn('Occupants', result)

        soup = BeautifulSoup(result, 'html5lib')
        element = soup.find(attrs={'data-autocomplete-input': True})
        self.assertIsNotNone(element)
        self.assertEqual(element['data-autocomplete-input-name'], 'occupants')
        self.assertJSONEqual(element['data-autocomplete-input-value'],
                             [{
                                 'pk': self.house_occupant.pk,
                                 'title': self.house_occupant.name,
                             }])
        self.assertNotIn('data-autocomplete-input-can-create', element.attrs)
        self.assertNotIn('data-autocomplete-input-is-single', element.attrs)

        # test the conversion of the form field's value from datadict
        field_value = element['data-autocomplete-input-value']
        form = form_class({'occupants': field_value}, instance=self.test_house)
        self.assertTrue(form.is_valid())
示例#4
0
class Composition(index.Indexed, models.Model):
    # Note: calling unescape on the title below is only ok because the input is
    # being sanitized by the RichTextField.
    title = RichTextField(features=['bold', 'italic'])
    composer = models.ForeignKey('Person',
                                 null=True,
                                 blank=False,
                                 on_delete=models.SET_NULL,
                                 related_name='+')

    def __str__(self):
        return unescape(strip_tags(self.title))

    def display_title(self):
        return str(self)

    def autocomplete_label(self):
        return "{} - {}".format(unescape(strip_tags(self.title)),
                                self.composer)

    panels = [
        FieldPanel('title'),
        AutocompletePanel('composer', target_model='main.Person')
    ]

    search_fields = [
        index.SearchField('title', partial_match=True),
        index.RelatedFields('composer', [
            index.SearchField('first_name', partial_match=True),
            index.SearchField('last_name', partial_match=True),
        ]),
    ]
示例#5
0
class ActionResponsibleParty(OrderedModel):
    action = ParentalKey(Action,
                         on_delete=models.CASCADE,
                         related_name='responsible_parties',
                         verbose_name=_('action'))
    organization = models.ForeignKey(
        Organization,
        on_delete=models.CASCADE,
        related_name='responsible_actions',
        limit_choices_to=Q(dissolution_date=None),
        verbose_name=_('organization'),
    )

    panels = [
        AutocompletePanel('organization'),
    ]

    class Meta:
        ordering = ['action', 'order']
        index_together = (('action', 'order'), )
        unique_together = (('action', 'organization'), )
        verbose_name = _('action responsible party')
        verbose_name_plural = _('action responsible parties')

    def __str__(self):
        return str(self.organization)
    def setUp(self):
        self.request = RequestFactory().get('/')
        user = AnonymousUser()
        self.request.user = user

        model = House  # a model with a foreign key to Person

        # a AutocompletePanel class that works on House's 'owner' field
        self.edit_handler = (ObjectList([AutocompletePanel('owner')
                                         ]).bind_to(model=House,
                                                    request=self.request))
        self.base_autocomplete_panel = self.edit_handler.children[0]

        # build a form class containing the fields that AutocompletePanel wants
        self.form_class = self.edit_handler.get_form_class()

        # a test instance of House with an owner and an occupant
        self.house_owner = Person.objects.create(name="An owner")
        self.house_occupant = Person.objects.create(name="An occupant")
        self.test_house = model.objects.create(owner=self.house_owner,
                                               occupants=[self.house_occupant])

        self.form = self.form_class(instance=self.test_house)
        self.autocomplete_panel = self.base_autocomplete_panel.bind_to(
            instance=self.test_house, form=self.form)
示例#7
0
class Subscription(ClusterableModel):
    RECURRING_STATUS_CHOICES = [
        (STATUS_ACTIVE, _(STATUS_ACTIVE.capitalize())),
        (STATUS_PROCESSING, _(STATUS_PROCESSING.capitalize())),
        (STATUS_PAUSED, _(STATUS_PAUSED.capitalize())),
        (STATUS_CANCELLED, _(STATUS_CANCELLED.capitalize())),
        (STATUS_INACTIVE, _(STATUS_INACTIVE.capitalize())),
    ]
    user = models.ForeignKey('newstream_user.User',
                             on_delete=models.SET_NULL,
                             null=True)
    gateway = models.ForeignKey('site_settings.PaymentGateway',
                                on_delete=models.SET_NULL,
                                null=True)
    is_test = models.BooleanField(default=False)
    profile_id = models.CharField(max_length=191, unique=True)
    recurring_amount = models.DecimalField(max_digits=20, decimal_places=2)
    currency = models.CharField(max_length=20)
    recurring_status = models.CharField(max_length=255,
                                        choices=RECURRING_STATUS_CHOICES,
                                        default=STATUS_INACTIVE,
                                        blank=True,
                                        null=True)
    subscribe_date = models.DateTimeField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    created_by = models.ForeignKey('newstream_user.User',
                                   related_name='subscription_created_by',
                                   on_delete=models.SET_NULL,
                                   blank=True,
                                   null=True)
    linked_user_deleted = models.BooleanField(default=False)
    deleted = models.BooleanField(default=False)

    panels = [
        AutocompletePanel('user', heading=_('User')),
        FieldPanel('gateway', heading=_('Payment Gateway')),
        FieldPanel('is_test', heading=_('Is Test Subscription?')),
        FieldPanel('profile_id', heading=_('Profile ID')),
        FieldPanel('recurring_amount', heading=_('Recurring Amount')),
        FieldPanel('currency', heading=_('Currency')),
        FieldPanel('recurring_status', heading=_('Recurring Status')),
        FieldPanel('subscribe_date', heading=_('Subscribe Date')),
        FieldPanel('linked_user_deleted', heading=_('Donor User Deleted?')),
    ]

    def __str__(self):
        return '#' + str(self.id) + ' - ' + self.profile_id

    def isRecurringCancelled(self):
        return True if self.recurring_status == STATUS_CANCELLED else False

    def isRecurringProcessing(self):
        return True if self.recurring_status == STATUS_PROCESSING else False

    class Meta:
        ordering = ['-subscribe_date', '-created_at']
        verbose_name = _('Subscription')
        verbose_name_plural = _('Subscriptions')
示例#8
0
class HomePage(Page):
    parent_page_type = ['wagtailcore.Page']
    subpage_types = [
        'blog.BlogIndexPage',
        'contact.ContactPage',
    ]
    banner_title = models.CharField(max_length=250, blank=False, null=True)
    banner_subtitle = RichTextField(features=['bold', 'italic'])
    banner_image = models.ForeignKey(
        "wagtailimages.Image",
        null=True,
        blank=False,
        on_delete=models.SET_NULL,
        related_name="+",
    )
    banner_cta = models.ForeignKey(
        "wagtailcore.Page",
        null=True,
        blank=False,
        on_delete=models.SET_NULL,
        related_name="+",
    )
    body = RichTextField(blank=True)
    api_fields = [
        APIField('banner_title'),
        APIField('banner_image'),
        APIField('banner_cta'),
        APIField('banner_subtitle')
    ]
    content_panels = Page.content_panels + [
        FieldPanel('banner_title'),
        FieldPanel('banner_subtitle'),
        ImageChooserPanel('banner_image'),
        AutocompletePanel('banner_cta'),
        FieldPanel('body', classname='full'),
        MultiFieldPanel([InlinePanel("carousel_images", max_num=5, min_num=1)],
                        heading='carousel images')
    ]
    custom_panels = [
        FieldPanel('banner_title'),
        FieldPanel('banner_subtitle'),
        ImageChooserPanel('banner_image'),
    ]

    # override edit_handler
    # edit_handler = TabbedInterface([
    #     ObjectList(content_panels, heading='Content'),
    #     ObjectList(Page.promote_panels, heading='Promote'),
    #     ObjectList(Page.settings_panels, heading='Settings'),
    #     ObjectList(custom_panels, heading='Sidebar Settings')
    # ])
    def get_context(self, request):
        context = super().get_context(request)
        blog_indexes = self.get_children().live().order_by(
            '-first_published_at')
        context['blog_indexes'] = blog_indexes
        return context
示例#9
0
class HostConnection(Orderable):
    event = ParentalKey("home.Event",
                        on_delete=models.CASCADE,
                        related_name="host_connections")
    speaker = models.ForeignKey(
        "home.Speaker",
        on_delete=models.CASCADE,
        null=True,
        blank=False,
        related_name="host_connections",
    )

    panels = [AutocompletePanel("speaker")]
示例#10
0
class BlogPageComment(Orderable):
    page = ParentalKey(BlogPage, on_delete=models.CASCADE, related_name='comments')
    posted = models.DateTimeField(default=timezone.now)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='blog_page_comments')
    name = models.CharField(blank=True, max_length=250)
    email = models.EmailField(blank=True, max_length=250)
    comment = models.TextField(blank=True, max_length=1250)

    panels = [
        FieldPanel('name'),
        FieldPanel('email'),
        AutocompletePanel('user', target_model=settings.AUTH_USER_MODEL),
        FieldPanel('posted'),
        FieldPanel('comment'),
    ]
    def test_render_create_as_field(self):
        edit_handler = (ObjectList([AutocompletePanel('group')
                                    ]).bind_to(model=Person,
                                               request=self.request))
        form_class = edit_handler.get_form_class()
        form = form_class(instance=self.house_occupant)
        autocomplete_panel = edit_handler.children[0].bind_to(
            instance=self.house_occupant, form=form, request=self.request)

        result = autocomplete_panel.render_as_field()
        self.assertIn('Group', result)

        soup = BeautifulSoup(result, 'html5lib')
        element = soup.find(attrs={'data-autocomplete-input': True})
        self.assertIsNotNone(element)
        self.assertEqual(element['data-autocomplete-input-name'], 'group')
        self.assertJSONEqual(element['data-autocomplete-input-value'], 'null')
        self.assertIn('data-autocomplete-input-can-create', element.attrs)
        self.assertIn('data-autocomplete-input-is-single', element.attrs)
示例#12
0
class MovieReview(BlogDetailPage):
    template = "blog/post.html"
    subpage_types = []
    parent_page_type = [
        "blog.BlogListingPage",
    ]

    movie = models.ForeignKey(
        "movies.Movie",
        blank=False,
        null=True,
        on_delete=models.SET_NULL,
    )

    content_panels = Page.content_panels + [
        SnippetChooserPanel("author"),
        AutocompletePanel("movie"),
        StreamFieldPanel("content"),
    ]
示例#13
0
class Campaign(ClusterableModel):
    title = I18nCharField(max_length=255)
    from_address = models.EmailField()
    recipients = models.ManyToManyField(TargetGroup)
    template = models.ForeignKey(EmailTemplate,
                                 on_delete=models.SET_NULL,
                                 null=True)
    sent = models.BooleanField(default=False, editable=False)
    sent_at = models.DateTimeField(blank=True, null=True)

    panels = [
        FieldPanel('title', heading=_('Title')),
        FieldPanel('from_address', heading=_('From Address')),
        FieldPanel('template', heading=_('Template')),
        AutocompletePanel('recipients', heading=_('Recipients')),
    ]

    def __str__(self):
        return str(self.title)

    class Meta:
        ordering = ['-id']
        verbose_name = _('Campaign')
        verbose_name_plural = _('Campaigns')
示例#14
0
class Site(ClusterableModel):
    name = models.CharField('Name', max_length=255, unique=True)
    slug = models.SlugField('Slug', unique=True, editable=False,
                            allow_unicode=True)

    domain = models.CharField(
        'Domain Name',
        max_length=255,
        unique=True,
        help_text='Specify the domain name without the scheme, '
                  'e.g. "example.com" instead of "https://example.com"')

    twitter_handle = models.CharField(
        'Twitter Handle',
        blank=True,
        max_length=16,
        help_text='Specify the twitter handle starting with "@"'
    )

    added = models.DateTimeField(auto_now_add=True)

    objects = models.Manager()
    scanned = ScannedSitesManager()

    regions = ParentalManyToManyField(
        'Region',
        blank=True,
        related_name='sites',
        help_text='Select which leaderboard you would like this '
                  'news site to appear on'
    )

    panels = [
        FieldPanel('name'),
        FieldPanel('domain'),
        FieldPanel('twitter_handle'),
        AutocompletePanel('regions', target_model='sites.Region'),
    ]

    class Meta:
        ordering = ['name']

    def __str__(self):
        return self.name

    def clean(self):
        self.slug = slugify(self.name, allow_unicode=True)
        if len(self.slug) == 0:
            raise ValidationError('Slug must not be an empty string')

    def save(self, *args, **kwargs):
        # Calling full_clean in save ensures that the slug will be
        # autogenerated and validated no matter what route the data
        # took to get into the database.
        # https://code.djangoproject.com/ticket/13100
        self.full_clean()
        super(Site, self).save(*args, **kwargs)

    def to_dict(self):
        """Generate a JSON-serializable dict of this object's attributes,
        including the results of the most recent scan."""
        # TODO optimize this (denormalize latest scan into Site?)
        return dict(
            name=self.name,
            domain=self.domain,
            absolute_url=self.get_absolute_url(),
            **self.scans.latest().to_dict()
        )

    def get_absolute_url(self):
        return reverse('sites:site', kwargs={'slug': self.slug})
示例#15
0
class AnswerPage(CFGOVPage):
    """Page type for Ask CFPB answers."""
    from ask_cfpb.models import Answer
    last_edited = models.DateField(
        blank=True,
        null=True,
        help_text="Change the date to today if you make a significant change.")
    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)."))
    short_answer = RichTextField(blank=True,
                                 features=['link', 'document-link'],
                                 help_text='Optional answer intro')
    answer_content = StreamField(ask_blocks.AskAnswerContent(),
                                 blank=True,
                                 verbose_name='Answer')
    answer_base = models.ForeignKey(Answer,
                                    blank=True,
                                    null=True,
                                    related_name='answer_pages',
                                    on_delete=models.SET_NULL)
    redirect_to_page = models.ForeignKey(
        'self',
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name='redirect_to_pages',
        help_text="Choose another AnswerPage to redirect this page to")
    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)
    category = models.ManyToManyField(
        'Category',
        blank=True,
        help_text=("Categorize this answer. "
                   "Avoid putting into more than one category."))
    search_tags = models.CharField(
        max_length=1000,
        blank=True,
        help_text="Search words or phrases, separated by commas")
    related_resource = models.ForeignKey(RelatedResource,
                                         blank=True,
                                         null=True,
                                         on_delete=models.SET_NULL)
    related_questions = ParentalManyToManyField(
        'self',
        symmetrical=False,
        blank=True,
        related_name='related_question',
        help_text='Maximum of 3 related questions')
    portal_topic = ParentalManyToManyField(
        PortalTopic,
        blank=True,
        help_text='Limit to 1 portal topic if possible')
    primary_portal_topic = ParentalKey(
        PortalTopic,
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name='primary_portal_topic',
        help_text=("Use only if assigning more than one portal topic, "
                   "to control which topic is used as a breadcrumb."))
    portal_category = ParentalManyToManyField(PortalCategory, blank=True)

    user_feedback = StreamField([
        ('feedback', v1_blocks.Feedback()),
    ],
                                blank=True)

    content_panels = CFGOVPage.content_panels + [
        MultiFieldPanel([
            FieldPanel('last_edited'),
            FieldPanel('question'),
            FieldPanel('statement'),
            FieldPanel('short_answer')
        ],
                        heading="Page content",
                        classname="collapsible"),
        StreamFieldPanel('answer_content'),
        MultiFieldPanel([
            SnippetChooserPanel('related_resource'),
            AutocompletePanel('related_questions',
                              page_type='ask_cfpb.AnswerPage',
                              is_single=False)
        ],
                        heading="Related resources",
                        classname="collapsible"),
        MultiFieldPanel([
            FieldPanel('portal_topic', widget=forms.CheckboxSelectMultiple),
            FieldPanel('primary_portal_topic'),
            FieldPanel('portal_category', widget=forms.CheckboxSelectMultiple)
        ],
                        heading="Portal tags",
                        classname="collapsible"),
        MultiFieldPanel([FieldPanel('featured')],
                        heading="Featured answer on Ask landing page",
                        classname="collapsible"),
        MultiFieldPanel([
            AutocompletePanel('redirect_to_page',
                              page_type='ask_cfpb.AnswerPage')
        ],
                        heading="Redirect to another answer",
                        classname="collapsible"),
        MultiFieldPanel([StreamFieldPanel('user_feedback')],
                        heading="User feedback",
                        classname="collapsible collapsed"),
    ]

    sidebar = StreamField([
        ('call_to_action', molecules.CallToAction()),
        ('related_links', molecules.RelatedLinks()),
        ('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)

    sidebar_panels = [
        StreamFieldPanel('sidebar'),
    ]

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

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

    template = 'ask-cfpb/answer-page.html'

    objects = CFGOVPageManager()

    def get_sibling_url(self):
        if self.answer_base:
            if self.language == 'es':
                sibling = self.answer_base.english_page
            else:
                sibling = self.answer_base.spanish_page
            if sibling and sibling.live and not sibling.redirect_to_page:
                return sibling.url

    def get_context(self, request, *args, **kwargs):
        portal_topic = self.primary_portal_topic or self.portal_topic.first()
        context = super(AnswerPage, self).get_context(request)
        context['related_questions'] = self.related_questions.all()
        context['description'] = (self.short_answer if self.short_answer else
                                  Truncator(self.answer_content).words(
                                      40, truncate=' ...'))
        context['last_edited'] = self.last_edited
        context['portal_page'] = get_portal_or_portal_search_page(
            portal_topic, language=self.language)
        context['breadcrumb_items'] = get_ask_breadcrumbs(
            language=self.language,
            portal_topic=portal_topic,
        )
        context['about_us'] = get_standard_text(self.language, 'about_us')
        context['disclaimer'] = get_standard_text(self.language, 'disclaimer')
        context['sibling_url'] = self.get_sibling_url()
        return context

    def __str__(self):
        if self.answer_base:
            return '{}: {}'.format(self.answer_base.id, self.title)
        else:
            return self.title

    @property
    def clean_search_tags(self):
        return [tag.strip() for tag in self.search_tags.split(',')]

    @property
    def status_string(self):
        if self.redirect_to_page:
            if not self.live:
                return ("redirected but not live")
            else:
                return ("redirected")
        else:
            return super(AnswerPage, self).status_string

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

        if not self.category.exists():
            return None

        return self.category.first().category_image

    # Overrides the default of page.id for comparing against split testing
    # clusters. See: core.feature_flags.in_split_testing_cluster
    @property
    def split_test_id(self):
        return self.answer_base.id
示例#16
0
class SingleAutocompletePage(Page):
    target = models.ForeignKey(TargetPage, on_delete=models.PROTECT)

    content_panels = Page.content_panels + [
        AutocompletePanel('target', page_type='testapp.TargetPage'),
    ]
示例#17
0
class Event(FixUrlMixin, Page):
    event_id = models.IntegerField(unique=True,
                                   null=True,
                                   blank=True,
                                   default=None)
    title_en = models.CharField(
        verbose_name=_("title"),
        max_length=255,
        blank=True,
        help_text=_(
            "The page title as you'd like it to be seen by the public"),
    )
    title_translated = TranslatedField("title", "title_en")
    short_overview_sk = models.CharField(
        max_length=255,
        blank=True,
    )
    short_overview_en = models.CharField(
        max_length=255,
        blank=True,
    )
    short_overview = TranslatedField("short_overview_sk", "short_overview_en")
    description_sk = RichTextField(blank=True)
    description_en = RichTextField(blank=True)
    description = TranslatedField("description_sk", "description_en")
    date_and_time = models.DateTimeField(default=timezone.now,
                                         verbose_name=_("date and time"))
    location = models.ForeignKey(
        "home.Location",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
    )
    video_url = models.URLField(
        null=True,
        blank=True,
        help_text=format_html(
            _("Supports Youtube, Vimeo and {}{}{}"),
            mark_safe(
                "<a href='https://github.com/wagtail/wagtail/blob/master/"
                "wagtail/embeds/oembed_providers.py' target='_blank'>"),
            _("other websites"),
            mark_safe("</a>"),
        ),
    )
    ticket_url = models.URLField(null=True, blank=True)
    category = models.ForeignKey(
        "home.Category",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
    )
    icon = models.ForeignKey(
        "wagtailimages.Image",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
    )
    show_on_festivalpage = models.BooleanField(default=False)
    wordpress_url = models.CharField(max_length=255,
                                     unique=True,
                                     null=True,
                                     blank=True)
    related_festival = models.ForeignKey(
        "home.FestivalPage",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
    )

    content_panels_sk = Page.content_panels + [
        MultiFieldPanel([
            FieldPanel("date_and_time"),
            AutocompletePanel("location"),
            FieldPanel("category"),
            PageChooserPanel("related_festival", "home.FestivalPage"),
            ImageChooserPanel("icon"),
        ]),
        MultiFieldPanel(
            [
                FieldPanel("short_overview_sk"),
                FieldPanel("description_sk"),
                FieldPanel("video_url"),
                FieldPanel("ticket_url"),
            ],
            heading=_("description"),
        ),
        InlinePanel("speaker_connections", heading="speakers"),
        InlinePanel("host_connections", heading="hosts"),
    ]
    content_panels_en = [
        FieldPanel("title_en", classname="full title"),
        MultiFieldPanel(
            [FieldPanel("short_overview_en"),
             FieldPanel("description_en")],
            heading=_("description"),
        ),
    ]
    promote_panels = Page.promote_panels + [
        FieldPanel("show_on_festivalpage"),
    ]
    edit_handler = TabbedInterface([
        ObjectList(content_panels_sk, heading="Content SK"),
        ObjectList(content_panels_en, heading="Content EN"),
        ObjectList(promote_panels, heading="Promote"),
        ObjectList(Page.settings_panels,
                   heading="Settings",
                   classname="settings"),
    ])

    parent_page_types = ["home.EventIndexPage"]
    subpage_types = []

    class Meta:
        verbose_name = _("event")
        verbose_name_plural = _("events")

    def get_url_parts(self, request=None):
        """Insert PK of object to url"""
        site_id, root_url, page_path = super().get_url_parts(request)
        page_path = page_path.split("/")
        page_path.insert(-2, str(self.event_id))
        return site_id, root_url, "/".join(page_path)

    def get_context(self, request, *args, **kwargs):
        from home.models.pages import last_festival

        context = super().get_context(request, *args, **kwargs)
        context["header_festival"] = last_festival(self)
        context["today"] = timezone.now().date()
        return context

    def save(self, *args, **kwargs):
        if self.event_id is None:
            last_event_id = (Event.objects.aggregate(
                Max("event_id"))["event_id__max"] or 0)
            self.event_id = last_event_id + 1
        return super().save(*args, **kwargs)

    @cached_property
    def speakers_limited(self):
        connections = list(self.speaker_connections.all())
        return {
            "under_limit": [c.speaker.title for c in connections[:3]],
            "over_limit_count":
            len(connections[3:]),
            "over_limit_names":
            ", ".join(c.speaker.title for c in connections[3:]),
        }

    def delete(self, *args, **kwargs):
        self.unpublish(user=kwargs.get("user"))
示例#18
0
class DonationForm(ClusterableModel):
    AMOUNT_TYPE_CHOICES = [
        ('fixed', _('Fixed Amount')),
        ('stepped', _('Fixed Steps')),
        ('custom', _('Custom Amount')),
        ('stepped_custom', _('Fixed Steps with Custom Amount Option')),
    ]
    DEFAULT_FREQ_CHOICES = [
        ('onetime', _('One-time')),
        ('monthly', _('Monthly')),
    ]
    title = I18nCharField(max_length=191, unique=True)
    description = I18nTextField(blank=True)
    default_frequency = models.CharField(max_length=20,
                                         choices=DEFAULT_FREQ_CHOICES,
                                         blank=False,
                                         default='onetime')
    amount_type = models.CharField(max_length=20, choices=AMOUNT_TYPE_CHOICES)
    fixed_amount = models.DecimalField(
        blank=True,
        null=True,
        max_digits=20,
        decimal_places=2,
        help_text=
        _('Define fixed donation amount if you chose "Fixed Amount" for your Amount Type.'
          ))
    allowed_gateways = models.ManyToManyField('site_settings.PaymentGateway')
    donation_footer_text = I18nRichTextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    deleted = models.BooleanField(default=False)

    panels = [
        FieldPanel('title', heading=_('Title')),
        FieldPanel('description', heading=_('Description')),
        FieldPanel('default_frequency',
                   heading=_('Default Donation Frequency'),
                   widget=RadioSelect),
        FieldPanel('amount_type', heading=_('Donation amount Type')),
        FieldPanel('fixed_amount', heading=_('Define Fixed Donation amount')),
        InlinePanel(
            'amount_steps',
            label=_('Fixed Amount Steps'),
            heading=_('Define Fixed Donation amount Steps'),
            help_text=
            _('Define fixed donation amount steps if you chose "Fixed Steps" for your Amount Type.'
              )),
        AutocompletePanel('allowed_gateways',
                          heading=_('Allowed Payment Gateways')),
        InlinePanel('donation_meta_fields',
                    label=_('Donation Meta Fields'),
                    heading=_('Donation Meta Fields')),
        FieldPanel(
            'donation_footer_text',
            heading=_('Footer Text(Under Donation Details Form)'),
            help_text=
            _('Footer text to be displayed under the "Donation Details" Form (Step 2 of payment)'
              ))
    ]

    class Meta:
        ordering = ['title']
        verbose_name = _('Donation Form')
        verbose_name_plural = _('Donation Forms')

    def __str__(self):
        return str(self.title)

    def isDefaultMonthly(self):
        return self.default_frequency == 'monthly'

    def isAmountFixed(self):
        return self.amount_type == 'fixed'

    def isAmountStepped(self):
        return self.amount_type == 'stepped'

    def isAmountCustom(self):
        return self.amount_type == 'custom'

    def isAmountSteppedCustom(self):
        return self.amount_type == 'stepped_custom'
示例#19
0
class ArticlePage(RoutablePageMixin, Page):
    headline = RichTextField(features=["italic"])
    subdeck = RichTextField(features=["italic"], null=True, blank=True)
    kicker = models.ForeignKey(Kicker,
                               null=True,
                               blank=True,
                               on_delete=models.PROTECT)
    body = StreamField(
        [
            ("paragraph", RichTextBlock()),
            ("photo", PhotoBlock()),
            ("photo_gallery", ListBlock(GalleryPhotoBlock(), icon="image")),
            ("embed", EmbeddedMediaBlock()),
            # ("form", FormBlock()),
        ],
        blank=True,
    )
    summary = RichTextField(
        features=["italic"],
        null=True,
        blank=True,
        help_text=
        "Displayed on the home page or other places to provide a taste of what the article is about.",
    )
    featured_image = models.ForeignKey(
        CustomImage,
        null=True,
        blank=True,
        on_delete=models.PROTECT,
        help_text="Shown at the top of the article and on the home page.",
    )
    featured_caption = RichTextField(features=["italic"],
                                     blank=True,
                                     null=True)

    Forms = models.ForeignKey(
        FormField,
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
    )
    content_panels = [
        MultiFieldPanel(
            [FieldPanel("headline", classname="title"),
             FieldPanel("subdeck")]),
        MultiFieldPanel(
            [
                InlinePanel(
                    "authors",
                    panels=[
                        AutocompletePanel("author",
                                          target_model="core.Contributor")
                    ],
                    label="Author",
                ),
                AutocompletePanel("kicker", target_model="core.Kicker"),
                ImageChooserPanel("featured_image"),
                FieldPanel("featured_caption"),
            ],
            heading="Metadata",
            classname="collapsible",
        ),
        FieldPanel("summary"),
        StreamFieldPanel("body"),
        FieldPanel("Forms"),
        # PageChooserPanel('feedback_form_page', ['base.FormPage']),
    ]

    search_fields = Page.search_fields + [
        index.SearchField("headline"),
        index.SearchField("subdeck"),
        index.SearchField("body"),
        index.SearchField("summary"),
        index.RelatedFields("kicker", [index.SearchField("title")]),
        index.SearchField("get_author_names", partial_match=True),
    ]

    subpage_types = []

    def clean(self):
        super().clean()

        soup = BeautifulSoup(self.headline, "html.parser")
        self.title = soup.text

    @route(r"^$")
    def post_404(self, request):
        """Return an HTTP 404 whenever the page is accessed directly.
        
        This is because it should instead by accessed by its date-based path,
        i.e. `<year>/<month>/<slug>/`."""
        raise Http404

    def set_url_path(self, parent):
        """Make sure the page knows its own path. The published date might not be set,
        so we have to take that into account and ignore it if so."""
        date = self.get_published_date() or timezone.now()
        self.url_path = f"{parent.url_path}{date.year}/{date.month:02d}/{self.slug}/"
        return self.url_path

    def serve_preview(self, request, mode_name):
        request.is_preview = True
        return self.serve(request)

    def get_context(self, request):
        context = super().get_context(request)
        context["authors"] = self.get_authors()
        return context

    def get_authors(self):
        return [r.author for r in self.authors.select_related("author")]

    def get_author_names(self):
        return [a.name for a in self.get_authors()]

    def get_published_date(self):
        return (self.go_live_at or self.first_published_at
                or getattr(self.get_latest_revision(), "created_at", None))

    def get_text_html(self):
        """Get the HTML that represents paragraphs within the article as a string."""
        builder = ""
        for block in self.body:
            if block.block_type == "paragraph":
                builder += str(block.value)
        return builder

    def get_plain_text(self):
        builder = ""
        soup = BeautifulSoup(self.get_text_html(), "html.parser")
        for para in soup.findAll("p"):
            builder += para.text
            builder += " "
        return builder[:-1]

    def get_related_articles(self):
        found_articles = []
        related_articles = []
        current_article_text = self.get_plain_text()
        if current_article_text is not None:
            current_article_words = set(current_article_text.split(" "))
            authors = self.get_authors()
            for author in authors:
                articles = author.get_articles()
                for article in articles:
                    if article.headline != self.headline:
                        text_to_match = article.get_plain_text()
                        article_words = set(text_to_match.split(" "))
                        found_articles.append((
                            article,
                            len(
                                list(
                                    current_article_words.intersection(
                                        article_words))),
                        ))
            found_articles.sort(key=operator.itemgetter(1), reverse=True)
            for i in range(min(4, len(found_articles))):
                related_articles.append(found_articles[i][0])
        return related_articles

    def get_first_chars(self, n=100):
        """Convert the body to HTML, extract the text, and then build
        a string out of it until we have at least n characters.
        If this isn't possible, then return None."""

        text = self.get_plain_text()
        if len(text) < n:
            return None

        punctuation = {".", "!"}
        for i in range(n, len(text)):
            if text[i] in punctuation:
                if i + 1 == len(text):
                    return text
                elif text[i + 1] == " ":
                    return text[:i + 1]

        return None

    def get_meta_tags(self):
        tags = {}
        tags["og:type"] = "article"
        tags["og:title"] = self.title
        tags["og:url"] = self.full_url
        tags["og:site_name"] = self.get_site().site_name

        # description: either the article's summary or first paragraph
        if self.summary is not None:
            tags["og:description"] = self.summary
            tags["twitter:description"] = self.summary
        else:
            first_chars = self.get_first_chars()
            if first_chars is not None:
                tags["og:description"] = first_chars
                tags["twitter:description"] = first_chars

        # image
        if self.featured_image is not None:
            # pylint: disable=E1101
            rendition = self.featured_image.get_rendition("min-1200x1200")
            rendition_url = self.get_site().root_url + rendition.url
            tags["og:image"] = rendition_url
            tags["twitter:image"] = rendition_url

        tags["twitter:site"] = "@rpipoly"
        tags["twitter:title"] = self.title
        if "twitter:description" in tags and "twitter:image" in tags:
            tags["twitter:card"] = "summary_large_image"
        else:
            tags["twitter:card"] = "summary"

        return tags
示例#20
0
class AnswerPage(CFGOVPage):
    """
    Page type for Ask CFPB answers.
    """
    from ask_cfpb.models import Answer
    last_edited = models.DateField(
        blank=True,
        null=True,
        help_text="Change the date to today if you make a significant change.")
    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)."))
    short_answer = RichTextField(blank=True, help_text='Optional answer intro')
    answer = RichTextField(
        blank=True,
        features=[
            'bold', 'italic', 'h2', 'h3', 'h4', 'link', 'ol', 'ul',
            'document-link', 'image', 'embed', 'ask-tips', 'edit-html'
        ],
        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. Re-select the content and click the button "
            "again to unstyle the tip."))
    answer_base = models.ForeignKey(Answer,
                                    blank=True,
                                    null=True,
                                    related_name='answer_pages',
                                    on_delete=models.SET_NULL)
    redirect_to = models.ForeignKey(
        Answer,
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name='redirected_pages',
        help_text="Choose another Answer to redirect this page to")
    redirect_to_page = models.ForeignKey(
        'self',
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name='redirect_to_pages',
        help_text="Choose another AnswerPage to redirect this page to")
    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)
    category = models.ManyToManyField(
        'Category',
        blank=True,
        help_text=("Categorize this answer. "
                   "Avoid putting into more than one category."))
    subcategory = models.ManyToManyField(
        'SubCategory',
        blank=True,
        help_text=("Choose only subcategories that belong "
                   "to one of the categories checked above."))
    search_tags = models.CharField(
        max_length=1000,
        blank=True,
        help_text="Search words or phrases, separated by commas")
    related_resource = models.ForeignKey(RelatedResource,
                                         blank=True,
                                         null=True,
                                         on_delete=models.SET_NULL)
    related_questions = ParentalManyToManyField(
        'self',
        symmetrical=False,
        blank=True,
        related_name='related_question',
        help_text='Maximum of 3 related questions')
    answer_id = models.IntegerField(default=0)
    portal_topic = ParentalManyToManyField(
        PortalTopic,
        blank=True,
        help_text='Limit to 1 portal topic if possible')
    primary_portal_topic = ParentalKey(
        PortalTopic,
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name='primary_portal_topic',
        help_text=("Use only if assigning more than one portal topic, "
                   "to control which topic is used as a breadcrumb."))
    portal_category = ParentalManyToManyField(PortalCategory, blank=True)

    user_feedback = StreamField([
        ('feedback', v1_blocks.Feedback()),
    ],
                                blank=True)

    content_panels = CFGOVPage.content_panels + [
        MultiFieldPanel([
            FieldPanel('last_edited'),
            FieldPanel('question'),
            FieldPanel('statement'),
            FieldPanel('short_answer'),
            FieldPanel('answer')
        ],
                        heading="Page content",
                        classname="collapsible"),
        MultiFieldPanel([
            SnippetChooserPanel('related_resource'),
            AutocompletePanel('related_questions',
                              page_type='ask_cfpb.AnswerPage',
                              is_single=False)
        ],
                        heading="Related resources",
                        classname="collapsible"),
        MultiFieldPanel([
            FieldPanel('portal_topic', widget=forms.CheckboxSelectMultiple),
            FieldPanel('primary_portal_topic'),
            FieldPanel('portal_category', widget=forms.CheckboxSelectMultiple)
        ],
                        heading="Portal tags",
                        classname="collapsible"),
        MultiFieldPanel([
            AutocompletePanel('redirect_to_page',
                              page_type='ask_cfpb.AnswerPage')
        ],
                        heading="Redirect to another answer",
                        classname="collapsible"),
        MultiFieldPanel([StreamFieldPanel('user_feedback')],
                        heading="User feedback",
                        classname="collapsible collapsed"),
    ]

    sidebar = StreamField([
        ('call_to_action', molecules.CallToAction()),
        ('related_links', molecules.RelatedLinks()),
        ('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)

    sidebar_panels = [
        StreamFieldPanel('sidebar'),
    ]

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

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(sidebar_panels, heading='Sidebar (English only)'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])

    objects = CFGOVPageManager()

    def get_context(self, request, *args, **kwargs):
        context = super(AnswerPage, self).get_context(request)
        context['related_questions'] = self.related_questions.all()
        context['description'] = self.short_answer if self.short_answer \
            else Truncator(self.answer).words(40, truncate=' ...')
        context['answer_id'] = self.answer_base.id
        if self.language == 'es':
            context['search_tags'] = self.clean_search_tags
            context['tweet_text'] = Truncator(self.question).chars(
                100, truncate=' ...')
            context['disclaimer'] = get_reusable_text_snippet(
                SPANISH_DISCLAIMER_SNIPPET_TITLE)
            context['category'] = self.category.first()
        elif self.language == 'en':
            context['about_us'] = get_reusable_text_snippet(
                ABOUT_US_SNIPPET_TITLE)
            context['disclaimer'] = get_reusable_text_snippet(
                ENGLISH_DISCLAIMER_SNIPPET_TITLE)
            context['last_edited'] = self.last_edited
            # breadcrumbs and/or category should reflect
            # the referrer if it is a consumer tools portal or
            # ask category page
            context['category'], context['breadcrumb_items'] = \
                get_question_referrer_data(
                    request, self.category.all())
            subcategories = []
            for subcat in self.subcategory.all():
                if subcat.parent == context['category']:
                    subcategories.append(subcat)
                for related in subcat.related_subcategories.all():
                    if related.parent == context['category']:
                        subcategories.append(related)
            context['subcategories'] = set(subcategories)

        return context

    def get_template(self, request):
        printable = request.GET.get('print', False)
        if self.language == 'es':
            if printable == 'true':
                return 'ask-cfpb/answer-page-spanish-printable.html'

            return 'ask-cfpb/answer-page-spanish.html'

        return 'ask-cfpb/answer-page.html'

    def __str__(self):
        if self.answer_base:
            return '{}: {}'.format(self.answer_base.id, self.title)
        else:
            return self.title

    @property
    def clean_search_tags(self):
        return [tag.strip() for tag in self.search_tags.split(',')]

    @property
    def status_string(self):
        if self.redirect_to_page:
            if not self.live:
                return ("redirected but not live")
            else:
                return ("redirected")
        else:
            return super(AnswerPage, self).status_string

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

        if not self.category.exists():
            return None

        return self.category.first().category_image

    # Overrides the default of page.id for comparing against split testing
    # clusters. See: core.feature_flags.in_split_testing_cluster
    @property
    def split_test_id(self):
        return self.answer_base.id
示例#21
0
class MlObjectPage(Page):
    disposer_id = models.ForeignKey('wagtailcore.Page',
                                    null=True,
                                    blank=True,
                                    on_delete=models.SET_NULL,
                                    related_name='+',
                                    verbose_name="распорядитель объекта")
    install_date = models.DateField("дата установки", blank=True, null=True)
    status_ok = models.BooleanField("параметры в норме", default=True)
    intro = models.TextField(verbose_name='краткое описание',
                             blank=True)
    description = RichTextField(blank=True, verbose_name='описание')
    tags = ClusterTaggableManager(through=MlObjectTag, blank=True, verbose_name='ключи поиска')
    auto_tags = ClusterTaggableManager(through=MlObjectAutoTag,
                                       blank=True,
                                       verbose_name='состояние',
                                       related_name='ml_obj_auto_tags',)

    # logical block
    # is system normally operating
    is_enabled = models.BooleanField(default=True, verbose_name='включен')
    # is critical for parent system
    is_critical = models.BooleanField(default=False, verbose_name='критически важен')
    # is object periodically diagnosed
    diagnosed = models.BooleanField(default=True, verbose_name='осмотрен')
    # have normal maintenance
    have_maintenance = models.BooleanField(default=True, verbose_name='обслужен')
    # object have cll-down to it's work
    call_down = models.BooleanField(default=False, verbose_name='есть замечания')
    # system working, but should be repaired
    have_to_be_repaired = models.BooleanField(default=False, verbose_name='требует ремонта')
    # if critical and critically broken, then parent system broken too
    is_critically_broken = models.BooleanField(default=False, verbose_name='сломан')

    # search block
    search_fields = Page.search_fields + [
        index.SearchField('intro'),
        index.SearchField('description'),
    ]

    def btn_status_action_on(self, obj):
        pass

    def btn_status_action_off(self, obj):
        pass

    btn_status = MlButton(
        symbol_on="✔",
        symbol_off=chr(9940),
        name_on="статус Ok",
        name_off="есть сбои",
        state_field=status_ok,
        alert_on="success",
        alert_off="danger",
        position=1,
        btn_action_on=btn_status_action_on,
        btn_action_off=btn_status_action_off
    )

    # logical block
    #    status                  symbol       description        alert      influence
    tag_dict = {
       'status_ok':             ("✔",       "статус Ok",        "success",  7,    # ✔
                                 # menu item (function name)  item description         influence
                                 {"status_ok_to_bad": ("изменить статус на 'есть сбои'", 10,),
                                  }
                                 ),
       'status_bad':            (chr(9940), "есть сбои",        "danger",   25,    # ⛔
                                 {"status_bad_to_ok": ("изменить статус на 'ОК'", 10,),
                                  }
                                 ),
       'is_enabled':            ("💡",       "включен",          "success",  5,     # 💡
                                 {"do_disable": ("выключить", 10,), }
                                 ),
       'is_disabled':           ("🔌",       "выключен",         "dark",    21,      # 🔌
                                 {"do_enable": ("включить", 10,),
                                  }
                                 ),
       'diagnosed':             (chr(9730),  "осмотрен",        "success",  3),    # ☂
       'have_to_be_diagnosed':  (chr(9200),  "пришло время осмотра", "info", 15,   # ⏰
                                 {"do_diagnose": ("выполнить осмотр", 10,),
                                  }
                                 ),
       'need_service':          (chr(9997),  "пришло время TO", "info",     18,   # ✍
                                 {"do_service": ("выполнить ТО", 10,),
                                  }
                                 ),
       'have_maintenance':      (chr(9874),  "прошел ТО",       "success",   2),  # ⚒
       'is_critical':           (chr(9889),  "критически важен", "success",  4),  # ⚡
       'call_down':             (chr(9785),  "есть замечания",  "warning",  20),  # ☹
       'have_to_be_repaired':   (chr(9888),  "требует ремонта", "warning",  23),  # ⚠
       'is_critically_broken':  (chr(9760),  "сломан",          "danger",   28),  # ☠
    }

    # getters and setters
    @property
    def status_bad(self):
        return not self.status_ok

    @status_bad.setter
    def status_bad(self, value):
        self.status_ok = not value
        self.save()

    @property
    def is_disabled(self):
        return not self.is_enabled

    @is_disabled.setter
    def is_disabled(self, value):
        self.is_enabled = not value
        self.save()

    @property
    def have_to_be_diagnosed(self):
        return not self.diagnosed

    @have_to_be_diagnosed.setter
    def have_to_be_diagnosed(self, value):
        self.diagnosed = not value
        self.save()

    @property
    def need_service(self):
        return not self.have_maintenance

    @need_service.setter
    def need_service(self, value):
        self.have_maintenance = not value
        self.save()

    # functions from menu items
    def status_ok_to_bad(self):
        pass

    def status_bad_to_ok(self):
        pass

    def do_disable(self):
        self.is_enabled = False
        self.save()

    def do_enable(self):
        self.is_enabled = True
        self.save()

    def do_diagnose(self):
        pass

    def do_service(self):
        pass


    # FixMe
    def main_image(self):
        gallery_item = self.gallery_images.first()
        if gallery_item:
            return gallery_item.image
        else:
            return None

    # colored borders for objects
    def ml_obj_border(self):
        tags = self.auto_tags.all()
        border = "success"
        influence = 0
        for tag in tags:
            for key in self.tag_dict:
                if self.tag_dict[key][0] == tag.name and influence < self.tag_dict[key][3]:
                    influence = self.tag_dict[key][3]
                    border = self.tag_dict[key][2]
        return border

    # override get_context method
    def get_context(self, request, *args, **kwargs):
        # check buttons ORM state on every page reload
        self.btn_status.get_orm_state(self)

        # Execute function by name. If name have '.', execute last part
        func_name = request.POST.get('func_name')
        execute_func(self, func_name)

        # execute main "get_context" function
        context = super().get_context(request)
        return context

    """
    # override save method
    def save(self, *args, **kwargs):

        if self.is_critically_broken:
            self.have_to_be_repaired = True

        if self.have_to_be_repaired:
            self.status_ok = False

        self.auto_tags.clear()

        status_args = ('status_ok', 'status_bad', 'is_enabled', 'is_disabled', 'diagnosed',
                       'have_to_be_diagnosed', 'have_maintenance', 'need_service', 'is_critical',
                       'call_down', 'have_to_be_repaired', 'is_critically_broken')
        for status in status_args:
            if getattr(self, status):
                self.auto_tags.add(obj_tag_dict[status][0])

        ################
        if crit and is_crit:
            ancestors = reversed(self.get_ancestors())
            for ancestor in ancestors:
                if ancestor.title != "root":
                    with ancestor.specific.is_critically_broken as crit_brok:

                        if crit_brok:
                            is_crit = ancestor.specific.is_critical
                            continue
                        alarm = True

                        with ancestor.specific.broken_if_all_elements_broken as brok:
                            if brok:
                                childr = ancestors.get_children().live()
                                for child in childr:
                                    if (child.specific.is_critically_broken == False) and \
                                            (child.specific.is_critical == True):
                                        alarm = False
                        if is_crit:
                            ancestor.specific.is_critically_broken = alarm
                            is_crit = ancestor.specific.is_critical
        """
        #TODO: change tags claster (self.tags.add('auto_alarm',"events"))
        #super(MlObjectPage, self).save()

    # control panels
    content_panels = Page.content_panels + [

        # Default field PageChooserPanel is changed to third party AutocompletePanel
        # PageChooserPanel('disposer_id', 'mall.MlObjDisposer'),
        MultiFieldPanel([
            AutocompletePanel('disposer_id', page_type='mall.MlObjDisposer'),
            FieldRowPanel([
                FieldPanel('install_date'),
                FieldPanel('status_ok'),
            ], classname=None),
        ], heading="Кто расроряжается:", classname="collapsible collapsed"),
        MultiFieldPanel([
            FieldRowPanel([
                FieldPanel('is_enabled'),
                FieldPanel('is_critical'),
            ], classname=None),
            FieldRowPanel([
                FieldPanel('diagnosed'),
                FieldPanel('have_maintenance'),
            ], classname=None),
            FieldRowPanel([
                FieldPanel('call_down'),
                FieldPanel('have_to_be_repaired'),
            ], classname=None),
            FieldRowPanel([
                FieldPanel('is_critically_broken', classname="col6"),
            ], classname=None),
        ], heading="Состояние объекта",
            classname="collapsible collapsed"),
        FieldPanel('tags'),
        FieldPanel('auto_tags'),
        FieldPanel('intro', classname="full"),
        MultiFieldPanel([
            FieldPanel('description', classname=None),
        ], heading="Описание",
            classname="full collapsible collapsed"),
        MultiFieldPanel([
            InlinePanel('ml_obj_docs', label="Приложение"),
        ], heading="Приложенные документы", classname="collapsible collapsed"),
        MultiFieldPanel([], heading='© Pavel Ushakov, BSD License'),
    ]

    promote_panels = Page.promote_panels + [
        MultiFieldPanel([
            InlinePanel('gallery_images', label="Фото объекта"),
        ], heading="Приложенные фото", classname="collapsible collapsed"),
        MultiFieldPanel([], heading='© Pavel Ushakov, BSD License'),
    ]

    settings_panels = Page.settings_panels + [


    ]

    parent_page_types = ['mall.MlObjectIndexPage']
    #subpage_types = []

    class Meta:
        verbose_name = "объект"
        verbose_name_plural = "объекты"
示例#22
0
class ExperiencePage(Page, CommentsMixIn):
    name = models.CharField(blank=True, default='', max_length=MAX_LENGTH)
    email = models.EmailField(blank=True, default='', max_length=MAX_LENGTH)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='experience_pages')
    posted = models.DateTimeField(default=timezone.now)

    experience = StreamField([
        ('heading', blocks.CharBlock(classname="full title")),
        ('paragraph', blocks.RichTextBlock()),
        ('image', ImageChooserBlock()),
        ('document', DocumentChooserBlock()),
    ])

    importance = models.CharField(max_length=6, choices=IMPORTANCE_CHOICES, default=NA)
    data_reliability = models.CharField(max_length=6, choices=IMPORTANCE_CHOICES, default=NA)
    action = StreamField([
        ('heading', blocks.CharBlock(classname="full title")),
        ('paragraph', blocks.RichTextBlock()),
        ('image', ImageChooserBlock()),
        ('document', DocumentChooserBlock()),
    ], blank=True)

    ranking_panels = [
        FieldPanel('importance'),
        FieldPanel('data_reliability'),
        StreamFieldPanel('action'),
    ]

    content_panels = Page.content_panels + [
        FieldPanel('name'),
        FieldPanel('email'),
        AutocompletePanel('user', target_model=settings.AUTH_USER_MODEL),
        FieldPanel('posted'),
        StreamFieldPanel('experience'),
        InlinePanel('comments', label="Comments"),
    ]

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

    subpage_types = [
    ]

    # parent_page_types = [
    #     'experiment.ExperimentPage',
    #     'homepage.ContentPage',
    # ]

    edit_handler = TabbedInterface(
        [
            ObjectList(content_panels, heading='Content'),
            ObjectList(ranking_panels, heading="Ranking"),
            ObjectList(Page.promote_panels, heading='Promote'),
            ObjectList(Page.settings_panels, heading='Settings'),
        ]
    )

    def __str__(self):
        return f'Experience Page {self.slug}'

    def serve(self, request):    
        from .forms import CommentForm
        return self.add_comments_and_return(request, CommentForm)
示例#23
0
class BlogPost(MetadataPageMixin, Page):
    author = models.ForeignKey('Person',
                               null=True,
                               blank=True,
                               on_delete=models.SET_NULL,
                               related_name='+')
    date = models.DateField()
    blog_image = models.ForeignKey('wagtailimages.Image',
                                   null=True,
                                   blank=True,
                                   on_delete=models.PROTECT,
                                   related_name='+')
    promo_copy = RichTextField(
        blank=True,
        features=['bold', 'italic'],
    )
    body = StreamField([
        ('paragraph', blocks.RichTextBlock()),
        ('image', ImageChooserBlock()),
        ('youtube_embed', YouTubeVideoBlock()),
    ])
    legacy_id = models.IntegerField(null=True, blank=True, unique=True)

    content_panels = Page.content_panels + [
        AutocompletePanel('author', target_model='main.Person'),
        FieldPanel('date'),
        ImageChooserPanel('blog_image'),
        FieldPanel('promo_copy'),
        StreamFieldPanel('body'),
    ]

    promote_panels = [
        MultiFieldPanel([
            FieldPanel('slug'),
            FieldPanel('seo_title'),
            FieldPanel('show_in_menus'),
            FieldPanel(
                'search_description',
                help_text=('Description to be used for social media sites. '
                           'Otherwise, the promo text will be used.')),
            ImageChooserPanel(
                'search_image',
                help_text=('Image to be used for social media sites. '
                           'Otherwise, the blog image will be used.')),
        ], ugettext_lazy('Common page configuration')),
    ]

    def get_meta_description(self):
        return self.search_description or self.promo_copy

    def get_meta_image(self):
        return self.search_image or self.blog_image

    def get_context(self, request):
        context = super().get_context(request)
        context['recent_blog_posts'] = BlogPost.objects.live().public()\
            .order_by('-date')[:5]
        return context

    parent_page_types = ['BlogIndex']
    subpage_types = []
示例#24
0
class Performer(Orderable):
    """
    This is a performer in the sense of an instance of a performance.
    """
    performance = ParentalKey('Performance',
                              on_delete=models.CASCADE,
                              related_name='performer')
    person = models.ForeignKey('Person',
                               null=False,
                               blank=False,
                               on_delete=models.PROTECT,
                               related_name='+')
    instrument = models.ForeignKey('InstrumentModel',
                                   null=False,
                                   blank=False,
                                   on_delete=models.PROTECT,
                                   related_name='+')

    def is_performer_live_public(self):
        return self.person.live and not self.person.get_view_restrictions()

    def get_performer(self):
        if (self.person.live and not self.person.get_view_restrictions()):
            return self.person

        return None

    def __str__(self):
        return "{} - {}".format(
            self.person.title,
            self.instrument,
        )

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)
        # If not saved as a concert performer already, then
        # create a concert performer
        concert = self.performance.get_parent().specific
        if not ConcertPerformer.objects.filter(concert__pk=concert.id,
                                               person=self.person).exists():
            ConcertPerformer.objects.create(concert=concert,
                                            person=self.person)

    def delete(self, *args, **kwargs):
        # Before deleting, check if other siblings list this person as a
        # performer, if so, than delete this particular performer, but leave
        # the concert performer alone.
        # If not, then delete the concert performer first, then delete
        concert = self.performance.get_parent().specific
        sibling_perfs = [
            p.specific for p in self.performance.get_siblings(inclusive=False)
        ]

        found = False
        for perf in sibling_perfs:
            for person in perf.performer.all():
                if person.id == self.person.id:
                    found = True
                    break

        if not found:
            try:
                ConcertPerformer.objects.get(concert__pk=concert.id,
                                             person=self.person).delete()
            except ConcertPerformer.DoesNotExist:
                pass

        super().delete(*args, **kwargs)

    panels = [
        AutocompletePanel('person', target_model='main.Person'),
        AutocompletePanel('instrument', target_model='main.InstrumentModel'),
    ]
示例#25
0
class AnswerPage(CFGOVPage):
    """Page type for Ask CFPB answers."""

    from ask_cfpb.models.django import Answer
    last_edited = models.DateField(
        blank=True,
        null=True,
        help_text="Change the date to today if you make a significant change.")
    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)."))
    short_answer = RichTextField(blank=True,
                                 features=['link', 'document-link'],
                                 help_text='Optional answer intro')
    answer_content = StreamField(ask_blocks.AskAnswerContent(),
                                 blank=True,
                                 verbose_name='Answer')
    answer_base = models.ForeignKey(Answer,
                                    blank=True,
                                    null=True,
                                    related_name='answer_pages',
                                    on_delete=models.SET_NULL)
    redirect_to_page = models.ForeignKey(
        'self',
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name='redirect_to_pages',
        help_text="Choose another AnswerPage to redirect this page to")
    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)
    category = models.ManyToManyField(
        'Category',
        blank=True,
        help_text=("Categorize this answer. "
                   "Avoid putting into more than one category."))
    search_tags = models.CharField(
        max_length=1000,
        blank=True,
        help_text="Search words or phrases, separated by commas")
    related_resource = models.ForeignKey(RelatedResource,
                                         blank=True,
                                         null=True,
                                         on_delete=models.SET_NULL)
    related_questions = ParentalManyToManyField(
        'self',
        symmetrical=False,
        blank=True,
        related_name='related_question',
        help_text='Maximum of 3 related questions')
    portal_topic = ParentalManyToManyField(
        PortalTopic,
        blank=True,
        help_text='Limit to 1 portal topic if possible')
    primary_portal_topic = ParentalKey(
        PortalTopic,
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name='primary_portal_topic',
        help_text=("Use only if assigning more than one portal topic, "
                   "to control which topic is used as a breadcrumb."))
    portal_category = ParentalManyToManyField(PortalCategory, blank=True)

    user_feedback = StreamField([
        ('feedback', v1_blocks.Feedback()),
    ],
                                blank=True)

    share_and_print = models.BooleanField(
        default=False,
        help_text="Include share and print buttons above answer.")

    content_panels = CFGOVPage.content_panels + [
        MultiFieldPanel([
            FieldPanel('last_edited'),
            FieldPanel('question'),
            FieldPanel('statement'),
            FieldPanel('short_answer')
        ],
                        heading="Page content",
                        classname="collapsible"),
        FieldPanel('share_and_print'),
        StreamFieldPanel('answer_content'),
        MultiFieldPanel([
            SnippetChooserPanel('related_resource'),
            AutocompletePanel('related_questions',
                              target_model='ask_cfpb.AnswerPage')
        ],
                        heading="Related resources",
                        classname="collapsible"),
        MultiFieldPanel([
            FieldPanel('portal_topic', widget=forms.CheckboxSelectMultiple),
            FieldPanel('primary_portal_topic'),
            FieldPanel('portal_category', widget=forms.CheckboxSelectMultiple)
        ],
                        heading="Portal tags",
                        classname="collapsible"),
        MultiFieldPanel([FieldPanel('featured')],
                        heading="Featured answer on Ask landing page",
                        classname="collapsible"),
        MultiFieldPanel([
            AutocompletePanel('redirect_to_page',
                              target_model='ask_cfpb.AnswerPage')
        ],
                        heading="Redirect to another answer",
                        classname="collapsible"),
        MultiFieldPanel([StreamFieldPanel('user_feedback')],
                        heading="User feedback",
                        classname="collapsible collapsed"),
    ]

    sidebar = StreamField([
        ('call_to_action', molecules.CallToAction()),
        ('related_links', molecules.RelatedLinks()),
        ('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)

    sidebar_panels = [
        StreamFieldPanel('sidebar'),
    ]

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

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

    template = 'ask-cfpb/answer-page.html'

    objects = CFGOVPageManager()

    def get_sibling_url(self):
        if self.answer_base:
            if self.language == 'es':
                sibling = self.answer_base.english_page
            else:
                sibling = self.answer_base.spanish_page
            if sibling and sibling.live and not sibling.redirect_to_page:
                return sibling.url

    def get_meta_description(self):
        """Determine what the page's meta and OpenGraph description should be

        Checks several different possible fields in order of preference.
        If none are found, returns an empty string, which is preferable to a
        generic description repeated on many pages.

        This method is overriding the standard one on CFGOVPage to factor in
        Ask CFPB AnswerPage-specific fields.
        """

        preference_order = [
            'search_description',
            'short_answer',
            'first_text',
        ]
        candidates = {}

        if self.search_description:
            candidates['search_description'] = self.search_description
        if self.short_answer:
            candidates['short_answer'] = strip_tags(self.short_answer)
        if hasattr(self, 'answer_content'):
            for block in self.answer_content:
                if block.block_type == 'text':
                    candidates['first_text'] = truncate_by_words_and_chars(
                        strip_tags(block.value['content'].source),
                        word_limit=35,
                        char_limit=160)
                    break

        for entry in preference_order:
            if candidates.get(entry):
                return candidates[entry]

        return ''

    def get_context(self, request, *args, **kwargs):
        # self.get_meta_description() is not called here because it is called
        # and added to the context by CFGOVPage's get_context() method.
        portal_topic = self.primary_portal_topic or self.portal_topic.first()
        context = super(AnswerPage, self).get_context(request)
        context['related_questions'] = self.related_questions.all()
        context['last_edited'] = self.last_edited
        context['portal_page'] = get_portal_or_portal_search_page(
            portal_topic, language=self.language)
        context['breadcrumb_items'] = get_ask_breadcrumbs(
            language=self.language,
            portal_topic=portal_topic,
        )
        context['about_us'] = get_standard_text(self.language, 'about_us')
        context['disclaimer'] = get_standard_text(self.language, 'disclaimer')
        context['sibling_url'] = self.get_sibling_url()
        return context

    def answer_content_text(self):
        raw_text = extract_raw_text(self.answer_content.stream_data)
        return strip_tags(" ".join([self.short_answer, raw_text]))

    def answer_content_data(self):
        return truncate_by_words_and_chars(self.answer_content_text())

    def short_answer_data(self):
        return ' '.join(
            RichTextField.get_searchable_content(self, self.short_answer))

    def text(self):
        short_answer = self.short_answer_data()
        answer_text = self.answer_content_text()
        full_text = f"{short_answer}\n\n{answer_text}\n\n{self.question}"
        return full_text

    def __str__(self):
        if self.answer_base:
            return f"{self.answer_base.id}: {self.title}"
        else:
            return self.title

    @property
    def clean_search_tags(self):
        return [tag.strip() for tag in self.search_tags.split(",")]

    @property
    def status_string(self):
        if self.redirect_to_page:
            if not self.live:
                return ("redirected but not live")
            else:
                return ("redirected")
        else:
            return super(AnswerPage, self).status_string

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

        if not self.category.exists():
            return None

        return self.category.first().category_image

    # Overrides the default of page.id for comparing against split testing
    # clusters. See: core.feature_flags.in_split_testing_cluster
    @property
    def split_test_id(self):
        return self.answer_base.id
 def test_target_model(self):
     autocomplete_panel = AutocompletePanel('owner').bind_to(model=House)
     self.assertEqual(autocomplete_panel.target_model, Person)
示例#27
0
class DirectoryEntry(MetadataPageMixin, Page):
    objects = DirectoryEntryManager()

    landing_page_url = models.URLField(
        'Landing page URL',
        max_length=255,
        unique=True
    )

    onion_address = models.CharField(
        'SecureDrop onion address',
        max_length=255,
        validators=[RegexValidator(regex=r'\.onion$', message="Enter a valid .onion address.")]
    )

    added = models.DateTimeField(auto_now_add=True)

    organization_logo = models.ForeignKey(
        'common.CustomImage',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
    )

    organization_logo_homepage = models.ForeignKey(
        'common.CustomImage',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text='Optional second logo optimized to show up on dark backgrounds. For instances that are featured on the homepage.'
    )

    organization_logo_is_title = models.BooleanField(
        default=False,
        help_text=(
            'Logo will be displayed instead of the header on page. Recommended '
            'primarily for logos containing the full organization name on a '
            'white or transparent background'
        )
    )

    organization_description = models.CharField(max_length=95, blank=True, null=True, help_text="A micro description of your organization that will be displayed in the directory.")

    languages = ParentalManyToManyField(
        'directory.Language',
        blank=True,
        verbose_name='Languages accepted',
        related_name='languages'
    )

    countries = ParentalManyToManyField(
        'directory.Country',
        blank=True,
        verbose_name='Countries',
        related_name='countries'
    )

    topics = ParentalManyToManyField(
        'directory.Topic',
        blank=True,
        verbose_name='Preferred topics',
        related_name='topics'
    )

    DELISTED_REASONS = (
        ('http', 'Mixed-content or no HTTPS'),
        ('no200', 'Non-200 status response'),
        ('down', 'Extended downtime (>1 week)'),
        ('other', 'Other'),
    )

    delisted = models.CharField(
        max_length=10,
        blank=True,
        null=True,
        choices=DELISTED_REASONS,
        default=None,
        help_text=('If set, entry will not show up in the directory, but the '
                   'page will still be live. Should be used for SecureDrop '
                   'instances that are under review for detected issues.')
    )

    WARNING_CHOICES = (
        ('no_cdn', 'Use of CDN'),
        ('no_third_party_assets', 'Use of analytics or third party assets'),
        ('subdomain', 'Subdomain'),
        ('referrer_policy_set_to_no_referrer', 'Referer Policy'),
        ('safe_onion_address', 'Links to Onion Addresses'),
    )

    permitted_domains_for_assets = ArrayField(
        models.TextField(),
        blank=True,
        default=list,
        help_text=('Comma-separated list of additional domains that will not trigger '
                   'the cross domain asset warning for this landing page.  '
                   'Subdomains on domains in this list are ignored.  For example, '
                   'adding "news.bbc.co.uk" permits all assets from "bbc.co.uk".'),
    )
    warnings_ignored = ChoiceArrayField(
        models.CharField(
            max_length=50,
            choices=WARNING_CHOICES,
        ),
        default=list,
        blank=True,
        help_text=('Landing page warnings that will not be shown to someone '
                   'viewing this entry, even if they are in the scan results. '
                   'Select multiples with shift or control click.'),
    )

    content_panels = Page.content_panels + [
        ReadOnlyPanel('added', heading='Date Added'),
        FieldPanel('landing_page_url'),
        FieldPanel('onion_address'),
        FieldPanel('organization_description'),
        MultiFieldPanel([
            ImageChooserPanel('organization_logo'),
            ImageChooserPanel('organization_logo_homepage'),
            FieldPanel('organization_logo_is_title'),
        ], 'Logo'),
        AutocompletePanel('languages', 'directory.Language', is_single=False),
        AutocompletePanel('countries', 'directory.Country', is_single=False),
        AutocompletePanel('topics', 'directory.Topic', is_single=False),
        InlinePanel('owners', label='Owners'),
        InlinePanel('results', label='Results'),
    ]

    settings_panels = Page.settings_panels + [
        FieldPanel('delisted'),
        FieldPanel('warnings_ignored'),
        FieldPanel('permitted_domains_for_assets'),
    ]

    search_fields_pgsql = ['title', 'landing_page_url', 'onion_address', 'organization_description']

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

        try:
            result = self.get_live_result()
        except ScanResult.DoesNotExist:
            return context

        if not result:
            return context

        context['show_warnings'] = True
        messages = []
        context['highest_warning_level'] = WarningLevel.NONE
        warnings = self.get_warnings(result)
        for warning in warnings:
            if warning.level.value > context['highest_warning_level'].value:
                context['highest_warning_level'] = warning.level

            messages.append(
                warning.message.format(
                    'This SecureDrop landing page',
                    domain=url_to_domain(result.landing_page_url),
                )
            )
        context['warning_messages'] = messages
        return context

    def serve(self, request):
        owners = [sd_owner.owner for sd_owner in self.owners.all()]
        if request.user in owners:
            self.editable = True
        else:
            self.editable = False

        return super(DirectoryEntry, self).serve(request)

    def get_live_result(self):
        # Used in template to get the latest live result.
        return self.results.filter(live=True).order_by('-result_last_seen').first()

    def get_warnings(self, result):
        warnings = []

        for warning in WARNINGS:
            if warning.name in self.warnings_ignored:
                continue
            if warning.test(result) == TestResult.FAIL:
                warnings.append(warning)
        return warnings

    def get_search_content(self):
        search_elements = get_search_content_by_fields(self, self.search_fields_pgsql)

        for field in ['languages', 'countries', 'topics']:
            for item in getattr(self, field).all():
                search_elements.append(item.title)

        return search_elements

    def save(self, *args, **kwargs):
        from directory.models import ScanResult
        super(DirectoryEntry, self).save(*args, **kwargs)
        ScanResult.objects.filter(landing_page_url=self.landing_page_url).update(securedrop=self)
示例#28
0
class Donation(ClusterableModel):
    PAYMENT_STATUS_CHOICES = [
        (STATUS_COMPLETE, _(STATUS_COMPLETE.capitalize())),
        (STATUS_PROCESSING, _(STATUS_PROCESSING.capitalize())),
        (STATUS_REFUNDED, _(STATUS_REFUNDED.capitalize())),
        (STATUS_REVOKED, _(STATUS_REVOKED.capitalize())),
        (STATUS_FAILED, _(STATUS_FAILED.capitalize())),
        (STATUS_CANCELLED, _(STATUS_CANCELLED.capitalize())),
    ]
    user = models.ForeignKey('newstream_user.User',
                             on_delete=models.SET_NULL,
                             blank=True,
                             null=True)
    form = models.ForeignKey('DonationForm',
                             on_delete=models.SET_NULL,
                             blank=True,
                             null=True)
    gateway = models.ForeignKey('site_settings.PaymentGateway',
                                on_delete=models.SET_NULL,
                                null=True)
    subscription = models.ForeignKey('Subscription',
                                     on_delete=models.SET_NULL,
                                     blank=True,
                                     null=True)
    is_test = models.BooleanField(default=False)
    transaction_id = models.CharField(max_length=191, unique=True)
    donation_amount = models.DecimalField(max_digits=20, decimal_places=2)
    is_recurring = models.BooleanField(default=False)
    currency = models.CharField(max_length=20)
    payment_status = models.CharField(max_length=255,
                                      choices=PAYMENT_STATUS_CHOICES)
    donation_date = models.DateTimeField()
    guest_email = models.EmailField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    created_by = models.ForeignKey('newstream_user.User',
                                   related_name='donation_created_by',
                                   on_delete=models.SET_NULL,
                                   blank=True,
                                   null=True)
    linked_user_deleted = models.BooleanField(default=False)
    deleted = models.BooleanField(default=False)

    panels = [
        AutocompletePanel('user', heading=_('User')),
        FieldPanel('form', heading=_('Donation Form')),
        FieldPanel('gateway', heading=_('Payment Gateway')),
        FieldPanel('subscription', heading=_('Subscription')),
        FieldPanel('is_test', heading=_('Is Test Donation?')),
        FieldPanel('transaction_id', heading=_('Transaction ID')),
        FieldPanel('donation_amount', heading=_('Donation amount')),
        FieldPanel('guest_email',
                   heading=_('Guest Email - for non-registered donors')),
        FieldPanel('is_recurring', heading=_('Is Recurring Donation?')),
        FieldPanel('currency', heading=_('Currency')),
        FieldPanel('payment_status', heading=_('Payment status')),
        FieldPanel('donation_date', heading=_('Donation Date')),
        InlinePanel(
            'metas',
            label=_('Donation Meta'),
            heading=_('Donation Meta Data'),
            help_text=_('Meta data about this donation is recorded here')),
        FieldPanel('linked_user_deleted', heading=_("Donor User Deleted?")),
    ]

    class Meta:
        ordering = ['-donation_date', '-created_at']
        verbose_name = _('Donation')
        verbose_name_plural = _('Donations')

    def __str__(self):
        return '#' + str(self.id) + ' - ' + self.transaction_id

    def isRecurring(self):
        return 'Yes' if self.is_recurring else 'No'

    def donor_name(self):
        if self.user:
            return self.user.fullname
        else:
            return self.guest_email

    @property
    def is_user_first_donation(self):
        resultSet = DonationPaymentMeta.objects.filter(
            donation_id=self.id, field_key='is_user_first_donation')
        if len(resultSet) == 1:
            return resultSet[0].field_value
        return False

    @is_user_first_donation.setter
    def is_user_first_donation(self, val):
        pass

    @property
    def donation_frequency(self):
        return 'Monthly' if self.is_recurring else 'One-time'

    @donation_frequency.setter
    def donation_frequency(self, val):
        pass

    @property
    def donation_type_stripe(self):
        return 'recurring' if self.is_recurring else 'one_time'

    @donation_type_stripe.setter
    def donation_type_stripe(self, val):
        pass

    def isOnGoing(self):
        return 'Yes' if self.is_recurring and self.subscription.recurring_status == STATUS_ACTIVE else 'No'
示例#29
0
class MlObjectIndexPage(Page):
    intro = models.TextField(verbose_name='краткое описание',
                             blank=True)
    # ToDo: rewrite categories to "orderable" model
    category = models.ForeignKey('mall.MlCategory',
                                 on_delete=models.SET_NULL,
                                 null=True,
                                 blank=True,
                                 related_name='+')
    # logical block
    # is system normally operating
    # TODO rewrite it to integer and change bages to represent cached data
    # TODO write metod to populate cashed data on descendants update
    status_ok = models.IntegerField(default=0, verbose_name='статус OK')
    status_bad = models.IntegerField(default=0, verbose_name='статус Bad')
    is_enabled = models.IntegerField(default=0, verbose_name='включено')
    is_disabled = models.IntegerField(default=0, verbose_name='выключено')
    # is object periodically diagnosed
    diagnosed = models.IntegerField(default=0, verbose_name='произведен осмотр')
    have_to_be_diagnosed = models.IntegerField(default=0, verbose_name='требует осмотра')
    # have normal maintenance
    have_maintenance = models.IntegerField(default=0, verbose_name='пройдено ТО')
    need_service = models.IntegerField(default=0, verbose_name='требует ТО')
    # object have call-down to it's work
    call_down = models.IntegerField(default=0, verbose_name='есть замечания')
    # system working, but should be repaired
    have_to_be_repaired = models.IntegerField(default=0, verbose_name='требует ремонта')

    # is critical for parent system
    is_critical = models.IntegerField(default=0, verbose_name='критически важный элемент')
    # if critical and critically broken, then parent system broken too
    is_critically_broken = models.IntegerField(default=0, verbose_name='сломано')
    # settings fields

    # group is visible for tags. If False - group close local scope of tags
    is_visible_for_tags = models.BooleanField(default=True, verbose_name="группа видна для дочерних меток")
    partial_cache_reset = models.BooleanField(default=False, verbose_name="частичный сброс кэшированных данных")
    full_cache_reset = models.BooleanField(default=False, verbose_name="полный сброс кэшированных данных")

    # overriding default get_context to include only live objects, ordered by title
    def get_context(self, request, *args, **kwargs):
        context = super().get_context(request)
        # can be ordered by published date: order_by('-first_published_at')
        ml_objects = self.get_children().live().order_by('title')
        context['ml_objects'] = ml_objects
        return context

    # colored borders for objects
    def ml_list_alert_color(self):
        alert = "border-success"
        influence = 0
        for key, value in obj_tag_dict.items():
            if hasattr(self, key):
                if getattr(self, key):
                    if influence < value[3]:
                        influence = value[3]
                        if key in ['status_ok', 'is_enabled', 'diagnosed', 'have_maintenance']:
                            alert = "border-" + value[2]
                        else:
                            alert = "alert-" + value[2]
        return alert

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

    content_panels = Page.content_panels + [
        FieldPanel('intro', classname="full"),
        # Default field SnippestChooserPanel is changed to third party AutocompletePanel
        # SnippetChooserPanel('category'),
        # PageChooserPanel('category'),
        AutocompletePanel('category', page_type='mall.MlCategory'),
        MultiFieldPanel([
            FieldRowPanel([
                FieldPanel('is_enabled'),
                FieldPanel('is_critical'),
            ], classname=None),
            FieldRowPanel([
                FieldPanel('diagnosed'),
                FieldPanel('have_maintenance'),
            ], classname=None),
            FieldRowPanel([
                FieldPanel('call_down'),
                FieldPanel('have_to_be_repaired'),
            ], classname=None),
            FieldRowPanel([
                FieldPanel('is_critically_broken', classname="col6"),
            ], classname=None),
        ], heading="Состояние объекта",
            classname="collapsible collapsed"),
        MultiFieldPanel([], heading='© Pavel Ushakov, BSD License'),
    ]

    settings_panels = Page.settings_panels + [
        FieldPanel('is_visible_for_tags'),
        FieldPanel('partial_cache_reset'),
        FieldPanel('full_cache_reset'),
    ]

    class Meta:
        verbose_name = "список объектов"
        verbose_name_plural = "списоки объектов"

    subpage_types = ['MlObjectPage', 'MlObjectIndexPage']