def add_blog_post(req, podcast_slug): site = get_site(req, podcast_slug) if not payment_plans.minimum( UserSettings.get_from_user(site.podcast.owner).plan, payment_plans.FEATURE_MIN_BLOG): raise Http404() data = {'site': site} if not req.POST: return _pmrender(req, 'dashboard/sites/blog/page_new.html', data) try: naive_publish = datetime.datetime.strptime(req.POST.get('publish'), '%Y-%m-%dT%H:%M') # 2015-07-09T12:00 adjusted_publish = naive_publish - UserSettings.get_from_user(req.user).get_tz_delta() post = SiteBlogPost( site=site, title=req.POST.get('title'), slug=req.POST.get('slug'), body=req.POST.get('body'), publish=adjusted_publish ) post.save() except Exception as e: print e data.update(error=True, default=req.POST) return _pmrender(req, 'dashboard/sites/blog/page_new.html', data) else: return redirect('site_manage_blog', podcast_slug=podcast_slug)
def podcast_dashboard(req, podcast_slug): pod = get_podcast(req, podcast_slug) tz = UserSettings.get_from_user(req.user).tz_offset total_listens = analytics_query.total_listens(pod) total_listens_this_week = analytics_query.total_listens_this_week(pod, tz) subscribers = analytics_query.total_subscribers(pod) data = { 'podcast': pod, 'episodes': pod.podcastepisode_set.order_by('-publish'), 'analytics': { 'total_listens': total_listens, 'total_listens_this_week': total_listens_this_week, 'subscribers': subscribers, }, 'next_milestone': next(x for x in constants.MILESTONES if x > total_listens), 'previous_milestone': [x for x in constants.MILESTONES if x <= total_listens][-1] if total_listens > 0 else 0, 'hit_first_milestone': total_listens > constants.MILESTONES[1], # The first "real" milestone 'is_still_importing': pod.is_still_importing(), 'site': None, 'LOCALES': constants.locales, 'SITE_PAGE_TYPES': SitePage.PAGE_TYPES, 'SITE_THEMES': Site.SITE_THEMES, 'N_DESTINATIONS': NotificationHook.DESTINATIONS, 'N_TRIGGERS': NotificationHook.TRIGGERS, } try: data['site'] = pod.site except Site.DoesNotExist: pass owner_uset = UserSettings.get_from_user(pod.owner) if payment_plans.minimum(owner_uset.plan, payment_plans.FEATURE_MIN_COMMENT_BOX): all_feedback = Feedback.objects.filter(podcast=pod) data['feedback_all'] = all_feedback data['feedback'] = all_feedback.filter(episode=None).order_by('-created') data['feedback_episodes'] = (all_feedback.exclude(episode=None) .annotate(Count('episode', distinct=True)) .select_related('episode')) if payment_plans.minimum(owner_uset.plan, payment_plans.PLAN_PRO): sparkline_data = analytics_query.get_episode_sparklines(pod, tz) data['sparklines'] = sparkline_data if payment_plans.minimum(owner_uset.plan, payment_plans.FEATURE_MIN_NOTIFICATIONS): data['notifications'] = NotificationHook.objects.filter(podcast=pod) if req.GET.get('notification_sent'): data['notification_sent'] = True if payment_plans.minimum(owner_uset.plan, payment_plans.FEATURE_MIN_COLLABORATORS): data['collab_error'] = req.GET.get('collaberr') return _pmrender(req, 'dashboard/podcast/page_podcast.html', data)
def set_coupon(req): code = req.POST.get('coupon') try: coupon = stripe.Coupon.retrieve(code) except stripe.error.InvalidRequestError: return redirect(reverse('upgrade') + '?coupon_invalid') if not coupon.valid: return redirect(reverse('upgrade') + '?coupon_invalid') if 'owner_id' in coupon.metadata: us = UserSettings.get_from_user(req.user) if us.plan != payment_plans.PLAN_DEMO: return redirect(reverse('upgrade') + '?coupon_unavailable') try: cust = us.get_stripe_customer() except Exception: pass else: if len(stripe.Invoice.list(customer=cust.id, limit=1).data): return redirect(reverse('upgrade') + '?coupon_unavailable') req.session['coupon'] = code return redirect(reverse('upgrade') + '?coupon_applied')
def _pmrender(req, template, data=None): data = data or {} class DefaultEmptyDict(collections.defaultdict): def __init__(self): super(DefaultEmptyDict, self).__init__(lambda: '') def get(self, _, d=''): return d data.setdefault('settings', settings) data.setdefault('default', DefaultEmptyDict()) data['sign'] = lambda x: signer.sign(x.encode('utf-8')).decode('utf-8') if x else x if not req.user.is_anonymous(): data.setdefault('user', req.user) networks = set(req.user.network_set.filter(deactivated=False)) data.setdefault('networks', networks) podcasts = set(req.user.podcast_set.all()) podcasts |= set(Podcast.objects.filter(networks__in=networks)) podcasts |= { x.podcast for x in Collaborator.objects.filter(collaborator=req.user).select_related('podcast')} data.setdefault('podcasts', list(podcasts)) uset = UserSettings.get_from_user(req.user) data.setdefault('user_settings', uset) data.setdefault('tz_delta', uset.get_tz_delta()) data.setdefault('max_upload_size', payment_plans.MAX_FILE_SIZE[uset.plan]) data['is_admin'] = req.user.is_staff and bool(req.GET.get('admin')) return render(req, template, data)
def get_html_description(self, is_demo=None): raw = self.description if is_demo is None: us = UserSettings.get_from_user(self.podcast.owner) is_demo = us.plan == payment_plans.PLAN_DEMO available_flags = self.podcast.get_available_flair_flags(flatten=True) if self.flair_tip_jar and FLAIR_TIP_JAR in available_flags: raw += "\n\nSupport %s by donating to the [tip jar](https://pinecast.com/payments/tips/%s)." % ( self.podcast.name, self.podcast.slug, ) if self.flair_site_link and FLAIR_SITE_LINK in available_flags: raw += "\n\nFind out more at [%s](http://%s.pinecast.co)." % (self.podcast.name, self.podcast.slug) if self.flair_feedback and FLAIR_FEEDBACK in available_flags: prompt = self.get_feedback_prompt() fb_url = "https://pinecast.com%s" % reverse( "ep_comment_box", podcast_slug=self.podcast.slug, episode_id=str(self.id) ) raw += "\n\n%s [%s](%s)" % (prompt, fb_url, fb_url) if is_demo or self.flair_powered_by and FLAIR_SITE_LINK in available_flags: raw += "\n\nThis podcast is powered by " "[Pinecast](https://pinecast.com)." markdown = gfm.markdown(raw) return sanitize(markdown)
def new_network(req): uset = UserSettings.get_from_user(req.user) if not plans.minimum(uset.plan, plans.FEATURE_MIN_NETWORK): return _pmrender(req, 'dashboard/network/page_new_upgrade.html') if not req.POST: return _pmrender(req, 'dashboard/network/page_new.html') try: net = Network( name=req.POST.get('name'), owner=req.user, image_url=signer.unsign(req.POST.get('image-url')) if req.POST.get('image-url') else None ) net.save() net.members.add(req.user) net.save() except Exception as e: print e return _pmrender(req, 'dashboard/network/page_new.html', {'error': ugettext('Error while saving network details'), 'default': req.POST}) return redirect('network_dashboard', network_id=net.id)
def edit_site(req, podcast_slug): site = get_site(req, podcast_slug) try: site.theme = req.POST.get('theme') site.cover_image_url = signer.unsign( req.POST.get('cover-url')) if req.POST.get('cover-url') else None site.logo_url = signer.unsign( req.POST.get('logo-url')) if req.POST.get('logo-url') else None site.analytics_id = req.POST.get('analytics_id') site.itunes_url = req.POST.get('itunes_url') site.stitcher_url = req.POST.get('stitcher_url') site.show_itunes_banner = req.POST.get('show_itunes_banner') == 'true' site.custom_css = req.POST.get('custom_css') site.custom_cname = req.POST.get('custom_cname') us = UserSettings.get_from_user(site.podcast.owner) if payment_plans.minimum(us.plan, payment_plans.FEATURE_MIN_BLOG): site.disqus_url = req.POST.get('disqus_url') if payment_plans.minimum(us.plan, payment_plans.FEATURE_MIN_SITE_FAVICON): site.favicon_url = signer.unsign(req.POST.get( 'favicon-url')) if req.POST.get('favicon-url') else None site.save() except Exception as e: return redirect( reverse('podcast_dashboard', podcast_slug=podcast_slug) + '?error=true#settings,site-options') else: return redirect( reverse('podcast_dashboard', podcast_slug=podcast_slug) + '#settings,site-options')
def podcast_top_episodes(req, podcast_slug): pod = get_podcast(req, podcast_slug) owner_uset = UserSettings.get_from_user(pod.owner) if not payment_plans.minimum(owner_uset.plan, payment_plans.FEATURE_MIN_COMMENT_BOX): return _pmrender(req, 'dashboard/podcast/page_top_episodes_upgrade.html', {'podcast': pod}) with analytics_query.AsyncContext() as async_ctx: top_ep_data_query = analytics_query.get_top_episodes(unicode(pod.id), async_ctx) top_ep_data = top_ep_data_query() ep_ids = [x['episode'] for x in top_ep_data] episodes = PodcastEpisode.objects.filter(id__in=ep_ids) mapped = {unicode(ep.id): ep for ep in episodes} # This step is necessary to filter out deleted episodes top_ep_data = [x for x in top_ep_data if x['episode'] in mapped] # Sort the top episode data descending top_ep_data = reversed(sorted(top_ep_data, key=lambda x: x['podcast'])) data = { 'podcast': pod, 'episodes': mapped, 'top_ep_data': top_ep_data, } return _pmrender(req, 'dashboard/podcast/page_top_episodes.html', data)
def edit_blog_post(req, podcast_slug, post_slug): site = get_site(req, podcast_slug) post = get_object_or_404(SiteBlogPost, site=site, slug=post_slug) if not req.POST: return _pmrender(req, 'dashboard/sites/blog/page_edit.html', { 'site': site, 'post': post }) try: naive_publish = datetime.datetime.strptime( req.POST.get('publish', '').split('.')[0], '%Y-%m-%dT%H:%M:%S') # 2015-07-09T12:00 adjusted_publish = naive_publish - UserSettings.get_from_user( req.user).get_tz_delta() post.title = req.POST.get('title') post.slug = req.POST.get('slug') post.body = req.POST.get('body') post.publish = adjusted_publish post.disable_comments = req.POST.get('disable_comments') == 'true' post.save() except Exception as e: data.update(error=True, default=req.POST) return _pmrender(req, 'dashboard/sites/blog/page_edit.html', data) else: return redirect( reverse('podcast_dashboard', podcast_slug=podcast_slug) + '#site,blog')
def send_tip(req, podcast_slug): pod = get_object_or_404(Podcast, slug=podcast_slug) try: amount = int(float(req.POST.get('amount')) / 100.0) * 100 if amount < 100: return {'error': ugettext('Tips less than $1 are not allowed.')} except Exception: return HttpResponse(status=400) tip_type = req.POST.get('type') owner_us = UserSettings.get_from_user(pod.owner) if owner_us.plan == PLAN_DEMO and tip_type == 'subscribe': return {'error': ugettext('You cannot have recurring tips for free podcasts.')} if amount > PLAN_TIP_LIMITS[owner_us.plan]: return {'error': ugettext('That tip is too large for %s') % pod.name} if tip_type == 'charge': return _send_one_time_tip(req, pod, owner_us, amount) elif tip_type == 'subscribe': return _auth_subscription(req, pod, amount) else: return HttpResponse(status=400)
def add_blog_post(req, podcast_slug): site = get_site(req, podcast_slug) if not payment_plans.minimum( UserSettings.get_from_user(site.podcast.owner).plan, payment_plans.FEATURE_MIN_BLOG): raise Http404() try: publis_parsed = datetime.datetime.strptime( req.POST.get('publish', '').split('.')[0], '%Y-%m-%dT%H:%M:%S') post = SiteBlogPost( site=site, title=req.POST.get('title'), slug=req.POST.get('slug'), body=req.POST.get('body'), publish=publis_parsed, disable_comments=req.POST.get('disable_comments') == 'true') post.save() except Exception as e: print(e) return redirect( reverse('podcast_dashboard', podcast_slug=podcast_slug) + '?error=sblog#site,blog') else: return redirect( reverse('podcast_dashboard', podcast_slug=podcast_slug) + '#site,blog')
def tip_flow(req, podcast_slug): pod = get_object_or_404(Podcast, slug=podcast_slug) us = UserSettings.get_from_user(pod.owner) if not us.stripe_payout_managed_account: if pod.homepage: return redirect(pod.homepage) else: raise Http404() recurring_tip = None pay_session = req.session.get('pay_session') tipper = None if pay_session: tipper = TipUser.objects.get(id=pay_session, verified=True) try: recurring_tip = RecurringTip.objects.get( podcast=pod, tipper=tipper, deactivated=False) except Exception as e: pass ctx = {'error': req.GET.get('error'), 'recurring_tip': recurring_tip, 'podcast': pod, 'tipper': tipper} return _pmrender(req, 'payments/tip_jar/main.html', ctx)
def get_html_description(self, is_demo=None): raw = self.description if is_demo is None: us = UserSettings.get_from_user(self.podcast.owner) is_demo = us.plan == payment_plans.PLAN_DEMO available_flags = self.podcast.get_available_flair_flags(flatten=True) if (self.flair_tip_jar and FLAIR_TIP_JAR in available_flags): raw += '\n\nSupport %s by donating to the [tip jar](https://pinecast.com/payments/tips/%s).' % ( self.podcast.name, self.podcast.slug) if (self.flair_site_link and FLAIR_SITE_LINK in available_flags): raw += '\n\nFind out more at [%s](http://%s.pinecast.co).' % ( self.podcast.name, self.podcast.slug) if (self.flair_feedback and FLAIR_FEEDBACK in available_flags): prompt = self.get_feedback_prompt() fb_url = 'https://pinecast.com%s' % reverse( 'ep_comment_box', podcast_slug=self.podcast.slug, episode_id=str(self.id)) raw += '\n\n%s [%s](%s)' % (prompt, fb_url, fb_url) if (is_demo or self.flair_powered_by and FLAIR_SITE_LINK in available_flags): raw += ('\n\nThis podcast is powered by ' '[Pinecast](https://pinecast.com).') markdown = gfm.markdown(raw) return sanitize(markdown)
def send_tip(req, podcast_slug): pod = get_object_or_404(Podcast, slug=podcast_slug) try: amount = int(float(req.POST.get('amount')) / 100.0) * 100 if amount < 100: return {'error': ugettext('Tips less than $1 are not allowed.')} except Exception: return HttpResponse(status=400) tip_type = req.POST.get('type') owner_us = UserSettings.get_from_user(pod.owner) if owner_us.plan == PLAN_DEMO and tip_type == 'subscribe': return {'error': ugettext('You cannot have recurring tips for free podcasts.')} if amount > PLAN_TIP_LIMITS[owner_us.plan]: return {'error': ugettext('That tip is too large for %s') % pod.name} if tip_type == 'charge': return _send_one_time_tip(req, pod, owner_us, amount) elif tip_type == 'subscribe': return _auth_subscription(req, pod, amount) else: return HttpResponse(status=400)
def new_site(req, podcast_slug): pod = get_podcast(req, podcast_slug) if not payment_plans.minimum( UserSettings.get_from_user(pod.owner).plan, payment_plans.FEATURE_MIN_SITES): raise Http404() try: site = Site( podcast=pod, theme=req.POST.get('theme'), cover_image_url=signer.unsign(req.POST.get('cover-url')) if req.POST.get('cover-url') else None, logo_url=signer.unsign(req.POST.get('logo-url')) if req.POST.get('logo-url') else None, analytics_id=req.POST.get('analytics_id'), itunes_url=req.POST.get('itunes_url'), stitcher_url=req.POST.get('stitcher_url'), show_itunes_banner=req.POST.get('show_itunes_banner') == 'true') site.save() except Exception as e: return redirect( reverse('podcast_dashboard', podcast_slug=podcast_slug) + '?error=true#site') else: return redirect( reverse('podcast_dashboard', podcast_slug=podcast_slug) + '#site')
def podcast_dashboard(req, podcast_slug): pod = get_podcast(req, podcast_slug) with analytics_query.AsyncContext() as async_ctx: total_listens = analytics_query.total_listens(pod, async_ctx) total_listens_this_week = analytics_query.total_listens_this_week(pod, async_ctx) subscribers = analytics_query.total_subscribers(pod, async_ctx) listens = total_listens() data = { 'podcast': pod, 'episodes': pod.podcastepisode_set.order_by('-publish'), 'analytics': { 'total_listens': listens, 'total_listens_this_week': total_listens_this_week(), 'subscribers': subscribers(), }, 'next_milestone': next(x for x in MILESTONES if x > listens), 'previous_milestone': [x for x in MILESTONES if x <= listens][-1] if listens else 0, 'hit_first_milestone': listens > MILESTONES[1], # The first "real" milestone 'is_still_importing': pod.is_still_importing(), } owner_uset = UserSettings.get_from_user(pod.owner) if payment_plans.minimum(owner_uset.plan, payment_plans.FEATURE_MIN_COMMENT_BOX): data['feedback'] = Feedback.objects.filter(podcast=pod, episode=None).order_by('-created') return _pmrender(req, 'dashboard/podcast/page_podcast.html', data)
def podcast_top_episodes(req, pod): timeframe = req.GET.get('timeframe') if not timeframe: return None tz = UserSettings.get_from_user(req.user).tz_offset top_ep_data = query.get_top_episodes(str(pod.id), timeframe, tz) episodes = PodcastEpisode.objects.filter(id__in=list(top_ep_data.keys())) mapped = {str(ep.id): ep for ep in episodes} # This step is necessary to filter out deleted episodes, since deleted episodes # are not removed from the analytics data. top_ep_data = {k: v for k, v in top_ep_data.items() if k in mapped} # Sort the top episode data descending return [[ugettext('Episode'), ugettext('Count')]] + [[ { 'href': reverse('podcast_episode', podcast_slug=pod.slug, episode_id=ep_id), 'title': mapped[ep_id].title, }, count, ] for ep_id, count in list( reversed(sorted(top_ep_data.items(), key=lambda x: x[1])))[:25]]
def podcast_geochart(req, podcast_slug): pod = get_podcast(req, podcast_slug) owner_uset = UserSettings.get_from_user(pod.owner) if not payment_plans.minimum(owner_uset.plan, payment_plans.FEATURE_MIN_GEOANALYTICS): return _pmrender(req, 'dashboard/podcast/page_geochart_upgrade.html', {'podcast': pod}) return _pmrender(req, 'dashboard/podcast/page_geochart.html', {'podcast': pod})
def new_site(req, podcast_slug): pod = get_podcast(req, podcast_slug) if not payment_plans.minimum( UserSettings.get_from_user(pod.owner).plan, payment_plans.FEATURE_MIN_SITES): raise Http404() data = { 'podcast': pod, 'themes': Site.SITE_THEMES, } if not req.POST: return _pmrender(req, 'dashboard/sites/page_new.html', data) try: site = Site( podcast=pod, theme=req.POST.get('theme'), cover_image_url=signer.unsign(req.POST.get('cover-url')) if req.POST.get('cover-url') else None, logo_url=signer.unsign(req.POST.get('logo-url')) if req.POST.get('logo-url') else None, analytics_id=req.POST.get('analytics_id'), itunes_url=req.POST.get('itunes_url'), stitcher_url=req.POST.get('stitcher_url') ) site.save() except Exception as e: print e data.update(error=True, default=req.POST) return _pmrender(req, 'dashboard/sites/page_new.html', data) else: return redirect('site_options', podcast_slug=podcast_slug)
def get_episodes(self): episodes = self.podcastepisode_set.filter(publish__lt=datetime.datetime.now(), awaiting_import=False).order_by( "-publish" ) if UserSettings.get_from_user(self.owner).plan == payment_plans.PLAN_DEMO: episodes = episodes[:10] return episodes
def new_podcast(req): uset = UserSettings.get_from_user(req.user) if payment_plans.has_reached_podcast_limit(uset): return _pmrender(req, 'dashboard/podcast/page_new_upgrade.html') ctx = {'PODCAST_CATEGORIES': json.dumps(list(CATEGORIES))} if not req.POST: return _pmrender(req, 'dashboard/podcast/page_new.html', ctx) try: pod = Podcast( slug=req.POST.get('slug'), name=req.POST.get('name'), subtitle=req.POST.get('subtitle'), cover_image=signer.unsign(req.POST.get('image-url')), description=req.POST.get('description'), is_explicit=req.POST.get('is_explicit', 'false') == 'true', homepage=req.POST.get('homepage'), language=req.POST.get('language'), copyright=req.POST.get('copyright'), author_name=req.POST.get('author_name'), owner=req.user) pod.save() # TODO: The following line can throw an exception and create a # duplicate podcast if something has gone really wrong pod.set_category_list(req.POST.get('categories')) except Exception as e: ctx.update(default=req.POST, error=True) return _pmrender(req, 'dashboard/podcast/page_new.html', ctx) return redirect('podcast_dashboard', podcast_slug=pod.slug)
def _pmrender(req, template, data=None): data = data or {} class DefaultEmptyDict(collections.defaultdict): def __init__(self): super(DefaultEmptyDict, self).__init__(lambda: '') def get(self, _, d=''): return d data.setdefault('settings', settings) data.setdefault('default', DefaultEmptyDict()) data['sign'] = lambda x: signer.sign(x) if x else x if not req.user.is_anonymous(): data.setdefault('user', req.user) networks = req.user.network_set.filter(deactivated=False) data.setdefault('networks', networks) podcasts = set(req.user.podcast_set.all()) for network in networks: for p in network.podcast_set.all(): podcasts.add(p) data.setdefault('podcasts', podcasts) uset = UserSettings.get_from_user(req.user) data.setdefault('user_settings', uset) data.setdefault('tz_delta', uset.get_tz_delta()) data.setdefault('max_upload_size', payment_plans.MAX_FILE_SIZE[uset.plan]) return render(req, template, data)
def _pmrender(req, template, data=None): data = data or {} class DefaultEmptyDict(collections.defaultdict): def __init__(self): super(DefaultEmptyDict, self).__init__(lambda: '') def get(self, _, d=''): return d data.setdefault('settings', settings) data.setdefault('default', DefaultEmptyDict()) data['sign'] = lambda x: signer.sign(x.encode('utf-8')).decode('utf-8') if x else x if not req.user.is_anonymous(): data.setdefault('user', req.user) networks = req.user.network_set.filter(deactivated=False) data.setdefault('networks', networks) podcasts = list(set( req.user.podcast_set.all() | Podcast.objects.filter(networks__in=networks))) data.setdefault('podcasts', podcasts) uset = UserSettings.get_from_user(req.user) data.setdefault('user_settings', uset) data.setdefault('tz_delta', uset.get_tz_delta()) data.setdefault('max_upload_size', payment_plans.MAX_FILE_SIZE[uset.plan]) data['is_admin'] = req.user.is_staff and bool(req.GET.get('admin')) return render(req, template, data)
def tip_flow(req, podcast_slug): pod = get_object_or_404(Podcast, slug=podcast_slug) us = UserSettings.get_from_user(pod.owner) if not us.stripe_payout_managed_account: if pod.homepage: return redirect(pod.homepage) else: raise Http404() recurring_tip = None pay_session = req.session.get('pay_session') tipper = None if pay_session: tipper = TipUser.objects.get(id=pay_session, verified=True) try: recurring_tip = RecurringTip.objects.get( podcast=pod, tipper=tipper, deactivated=False) except Exception as e: pass ctx = {'error': req.GET.get('error'), 'recurring_tip': recurring_tip, 'podcast': pod, 'tipper': tipper} return _pmrender(req, 'payments/tip_jar/main.html', ctx)
def importer(req): uset = UserSettings.get_from_user(req.user) if uset.plan == plans.PLAN_DEMO: return _pmrender(req, 'dashboard/page_importer_upgrade.html', {'reached_limit': False}) elif plans.has_reached_podcast_limit(uset): return _pmrender(req, 'dashboard/page_importer_upgrade.html', {'reached_limit': True}) else: return _pmrender(req, 'dashboard/page_importer.html')
def get_subscription(self): us = UserSettings.get_from_user(self.podcast.owner) stripe_account = us.stripe_payout_managed_account try: return stripe.Subscription.retrieve( self.stripe_subscription_id, stripe_account=stripe_account) except stripe.error.InvalidRequestError: return None
def get_subscription(self): us = UserSettings.get_from_user(self.podcast.owner) stripe_account = us.stripe_payout_managed_account try: return stripe.Subscription.retrieve(self.stripe_subscription_id, stripe_account=stripe_account) except stripe.error.InvalidRequestError: return None
def episode_geochart(req, podcast_slug, episode_id): pod = get_podcast(req, podcast_slug) owner_uset = UserSettings.get_from_user(pod.owner) ep = get_object_or_404(PodcastEpisode, podcast=pod, id=episode_id) if not payment_plans.minimum(owner_uset.plan, payment_plans.FEATURE_MIN_GEOANALYTICS_EP): return _pmrender(req, 'dashboard/episode/page_geochart_upgrade.html', {'podcast': pod, 'episode': ep}) return _pmrender(req, 'dashboard/episode/page_geochart.html', {'podcast': pod, 'episode': ep})
def get_episodes(self): episodes = self.get_all_episodes_raw().filter( publish__lt=round_now(), awaiting_import=False).order_by('-publish') us = UserSettings.get_from_user(self.owner) if us.plan == payment_plans.PLAN_DEMO: episodes = episodes[:10] return episodes
def get_domain(self): if not self.custom_cname: return self.get_subdomain() us = UserSettings.get_from_user(self.podcast.owner) if not minimum(us.plan, FEATURE_MIN_SITES): return self.get_subdomain() return "http://%s" % self.custom_cname
def get_domain(self): if not self.custom_cname: return self.get_subdomain() us = UserSettings.get_from_user(self.podcast.owner) if not minimum(us.plan, FEATURE_MIN_SITES): return self.get_subdomain() return 'http://%s' % self.custom_cname
def upgrade(req): us = UserSettings.get_from_user(req.user) customer = us.get_stripe_customer() ctx = { 'stripe_customer': customer, } return _pmrender(req, 'payments/main.html', ctx)
def set_payment_method(req): us = UserSettings.get_from_user(req.user) customer = us.get_stripe_customer() if customer: customer.source = req.POST.get('token') customer.save() else: us.create_stripe_customer(req.POST.get('token')) return {'success': True, 'id': us.stripe_customer_id}
def manage_blog(req, podcast_slug): site = get_site(req, podcast_slug) if not payment_plans.minimum( UserSettings.get_from_user(site.podcast.owner).plan, payment_plans.FEATURE_MIN_BLOG): raise Http404() return _pmrender(req, 'dashboard/sites/blog/page_manage.html', {'site': site, 'posts': site.siteblogpost_set.all().order_by('-publish')})
def get_remaining_surge(self, max_size): uset = UserSettings.get_from_user(self.owner) if not payment_plans.minimum(uset.plan, payment_plans.PLAN_STARTER): return 0 thirty_ago = datetime.datetime.now() - datetime.timedelta(days=30) last_thirty_eps = self.podcastepisode_set.filter(created__gt=thirty_ago, audio_size__gt=max_size) surge_count = last_thirty_eps.count() surge_amt = last_thirty_eps.aggregate(models.Sum("audio_size"))["audio_size__sum"] or 0 surge_amt -= surge_count * max_size remaining = max_size - surge_amt return 0 if remaining < 0 else remaining
def favicon(req, podcast_slug): pod = get_object_or_404(Podcast, slug=podcast_slug) us = UserSettings.get_from_user(pod.owner) if not minimum(us.plan, FEATURE_MIN_SITE_FAVICON): return redirect('https://pinecast.com/static/img/favicon.png') site = get_object_or_404(models.Site, podcast=pod) if not site.favicon_url: return redirect('https://pinecast.com/static/img/favicon.png') return redirect(site.favicon_url)
def favicon(req, podcast_slug): pod = get_object_or_404(Podcast, slug=podcast_slug) us = UserSettings.get_from_user(pod.owner) if not minimum(us.plan, FEATURE_MIN_SITE_FAVICON): return redirect('https://pinecast.com/static/img/favicon.png') site = get_object_or_404(models.Site, podcast=pod) if not site.favicon_url: return redirect('https://pinecast.com/static/img/favicon.png') return redirect(site.favicon_url)
def podcast_new_ep(req, podcast_slug): pod = get_podcast(req, podcast_slug) tz_delta = UserSettings.get_from_user(req.user).get_tz_delta() latest_episode = pod.get_most_recent_episode() ctx = { 'podcast': pod, 'latest_ep': latest_episode, } if not req.POST: base_default = EmptyStringDefaultDict() base_default['publish'] = datetime.datetime.strftime( datetime.datetime.now() + tz_delta, '%Y-%m-%dT%H:%M' # 2015-07-09T12:00 ) ctx['default'] = base_default return _pmrender(req, 'dashboard/episode/page_new.html', ctx) try: publish_parsed = datetime.datetime.strptime(req.POST.get('publish').split('.')[0], ISO_FORMAT) image_url = req.POST.get('image-url') ep = PodcastEpisode( podcast=pod, title=req.POST.get('title'), subtitle=req.POST.get('subtitle'), publish=publish_parsed, description=req.POST.get('description'), duration=int(req.POST.get('duration-hours') or 0) * 3600 + int(req.POST.get('duration-minutes') or 0) * 60 + int(req.POST.get('duration-seconds') or 0), audio_url=signer.unsign(req.POST.get('audio-url')), audio_size=int(req.POST.get('audio-url-size')), audio_type=req.POST.get('audio-url-type'), image_url=signer.unsign(image_url) if image_url else pod.cover_image, copyright=req.POST.get('copyright'), license=req.POST.get('license'), explicit_override=req.POST.get('explicit_override')) ep.set_flair(req.POST, no_save=True) ep.save() if req.POST.get('feedback_prompt'): prompt = EpisodeFeedbackPrompt(episode=ep, prompt=req.POST.get('feedback_prompt')) prompt.save() except Exception as e: rollbar.report_exc_info(sys.exc_info(), req) ctx['error'] = True ctx['default'] = req.POST return _pmrender(req, 'dashboard/episode/page_new.html', ctx) return redirect('podcast_dashboard', podcast_slug=pod.slug)
def _process(self): assert self.selection select = ', '.join( select_format(k, v) for k, v in self.selection.items()) where = '' group_by = '' tz = UserSettings.get_from_user(self.req.user).tz_offset if self.criteria: where = ' AND '.join( where_format(k, v) for k, v in self.criteria.items() ) if self.group_by: if isinstance(self.group_by, (list, tuple)): group_by = ', '.join(ident(x) for x in self.group_by) else: group_by = ident(self.group_by) if self.timeframe: tf = USER_TIMEFRAMES.get( self.req.GET.get('timeframe', self.timeframe), lambda tz: None)(tz) if tf: if where: where += ' AND ' where += tf if self.interval_val: if group_by: group_by += ', ' group_by += INTERVALS[self.interval_val] query = 'SELECT %s FROM %s' % (select, ident(self.event_type)) if where: query += ' WHERE %s' % where if group_by: query += ' GROUP BY %s' % group_by query += ';' # if settings.DEBUG: # print(query) self.res = get_client().query(query, database=self.db) return self
def podcast_new_ep(req, podcast_slug): pod = get_podcast(req, podcast_slug) tz_delta = UserSettings.get_from_user(req.user).get_tz_delta() latest_episode = pod.get_most_recent_episode() ctx = { 'podcast': pod, 'latest_ep': latest_episode, } if not req.POST: base_default = EmptyStringDefaultDict() base_default['publish'] = datetime.datetime.strftime( datetime.datetime.now() + tz_delta, '%Y-%m-%dT%H:%M' # 2015-07-09T12:00 ) ctx['default'] = base_default return _pmrender(req, 'dashboard/episode/page_new.html', ctx) try: publish_parsed = datetime.datetime.strptime(req.POST.get('publish').split('.')[0], ISO_FORMAT) image_url = req.POST.get('image-url') ep = PodcastEpisode( podcast=pod, title=req.POST.get('title'), subtitle=req.POST.get('subtitle'), publish=publish_parsed, description=req.POST.get('description'), duration=int(req.POST.get('duration-hours')) * 3600 + int(req.POST.get('duration-minutes')) * 60 + int(req.POST.get('duration-seconds')), audio_url=signer.unsign(req.POST.get('audio-url')), audio_size=int(req.POST.get('audio-url-size')), audio_type=req.POST.get('audio-url-type'), image_url=signer.unsign(image_url) if image_url else pod.cover_image, copyright=req.POST.get('copyright'), license=req.POST.get('license'), explicit_override=req.POST.get('explicit_override')) ep.set_flair(req.POST, no_save=True) ep.save() if req.POST.get('feedback_prompt'): prompt = EpisodeFeedbackPrompt(episode=ep, prompt=req.POST.get('feedback_prompt')) prompt.save() except Exception as e: raise e ctx['error'] = True ctx['default'] = req.POST return _pmrender(req, 'dashboard/episode/page_new.html', ctx) return redirect('podcast_dashboard', podcast_slug=pod.slug)
def cancel(self): us = UserSettings.get_from_user(self.podcast.owner) try: subscription = stripe.Subscription.retrieve( self.stripe_subscription_id, stripe_account=us.stripe_payout_managed_account) except stripe.error.InvalidRequestError: pass else: subscription.delete() finally: self.deactivated = True self.save()
def cancel(self): us = UserSettings.get_from_user(self.podcast.owner) try: subscription = stripe.Subscription.retrieve( self.stripe_subscription_id, stripe_account=us.stripe_payout_managed_account) except stripe.error.InvalidRequestError: pass else: subscription.delete() finally: self.deactivated = True self.save()
def signup(req): if not req.user.is_anonymous(): return redirect('dashboard') if not req.POST: return _pmrender( req, 'signup/main.html', {'email': req.GET.get('email', '')} ) error = None if not _validate_recaptcha(req): error = ugettext('Your humanity was not verified') elif not req.POST.get('email'): error = ugettext('Missing email address') elif not req.POST.get('password'): error = ugettext('Come on, you need a password') elif len(req.POST.get('password')) < 8: error = ugettext('Your password needs to be at least 8 characters long') elif User.objects.filter(email=req.POST.get('email')).count(): error = ugettext('That email address is already associated with an account') if error: return _pmrender(req, 'signup/main.html', { 'error': error, 'email': req.POST.get('email'), }) try: u = User.objects.create_user( str(uuid.uuid4())[:30], req.POST.get('email'), req.POST.get('password') ) u.save() except Exception as e: return _pmrender(req, 'signup/main.html', { 'error': str(e), 'email': req.POST.get('email'), }) try: us = UserSettings.get_from_user(u) us.tz_offset = req.POST.get('timezone') us.save() except Exception: pass # whatever. return redirect(reverse('login') + '?signup_success=true')
def process_request(self, req): scheme = 'http' if not req.is_secure() else 'https' domain = req.META.get('HTTP_HOST') or req.META.get('SERVER_NAME') if settings.DEBUG and ':' in domain: domain = domain[:domain.index(':')] pc_forward = req.META.get('HTTP_X_PINECAST_FORWARD') if pc_forward: try: site = Site.objects.get(custom_cname__iexact=pc_forward) us = UserSettings.get_from_user(site.podcast.owner) if not minimum(us.plan, FEATURE_MIN_CNAME): raise NotCNAMEReadyException() return self._resolve(req, site.podcast.slug) except (Site.DoesNotExist, NotCNAMEReadyException): pass pieces = domain.split('.') if len(pieces) != 3: return None if domain[len(pieces[0]):] not in SUBDOMAIN_HOSTS: return None try: pod = Podcast.objects.get(slug__iexact=pieces[0]) site = Site.objects.get(podcast=pod) except (Site.DoesNotExist, Podcast.DoesNotExist): return None us = UserSettings.get_from_user(pod.owner) if minimum(us.plan, FEATURE_MIN_CNAME) and site.custom_cname: return redirect('%s://%s%s' % (scheme, site.custom_cname, req.get_full_path()), permanent=True) return self._resolve(req, pod.slug)
def process_request(self, req): scheme = 'http' if not req.is_secure() else 'https' domain = req.META.get('HTTP_HOST') or req.META.get('SERVER_NAME') if settings.DEBUG and ':' in domain: domain = domain[:domain.index(':')] pc_forward = req.META.get('HTTP_X_PINECAST_FORWARD') if pc_forward: try: site = Site.objects.get(custom_cname__iexact=pc_forward) us = UserSettings.get_from_user(site.podcast.owner) if not minimum(us.plan, FEATURE_MIN_CNAME): raise NotCNAMEReadyException() return self._resolve(req, site.podcast.slug) except (Site.DoesNotExist, NotCNAMEReadyException): pass pieces = domain.split('.') if len(pieces) != 3: return None if domain[len(pieces[0]):] not in SUBDOMAIN_HOSTS: return None try: pod = Podcast.objects.get(slug__iexact=pieces[0]) site = Site.objects.get(podcast=pod) except (Site.DoesNotExist, Podcast.DoesNotExist): return None us = UserSettings.get_from_user(pod.owner) if minimum(us.plan, FEATURE_MIN_CNAME) and site.custom_cname: return redirect( '%s://%s%s' % (scheme, site.custom_cname, req.get_full_path()), permanent=True) return self._resolve(req, pod.slug)
def upgrade(req): us = UserSettings.get_from_user(req.user) customer = us.get_stripe_customer() ctx = { 'active_coupon': req.session.get('coupon'), 'coupon_applied': 'coupon_applied' in req.GET, 'coupon_invalid': 'coupon_invalid' in req.GET, 'coupon_unavailable': 'coupon_unavailable' in req.GET, 'error': req.GET.get('error'), 'stripe_customer': customer, 'success': 'success' in req.GET, } return _pmrender(req, 'payments/main.html', ctx)
def get_remaining_surge(self, max_size): uset = UserSettings.get_from_user(self.owner) if not payment_plans.minimum(uset.plan, payment_plans.PLAN_STARTER): return 0 thirty_ago = datetime.datetime.now() - datetime.timedelta(days=30) last_thirty_eps = self.podcastepisode_set.filter( created__gt=thirty_ago, audio_size__gt=max_size) surge_count = last_thirty_eps.count() surge_amt = last_thirty_eps.aggregate( models.Sum('audio_size'))['audio_size__sum'] or 0 surge_amt -= surge_count * max_size remaining = max_size - surge_amt return 0 if remaining < 0 else remaining
def _process(self): assert self.selection select = ', '.join( select_format(k, v) for k, v in self.selection.items()) where = '' group_by = '' tz = UserSettings.get_from_user(self.req.user).tz_offset if self.criteria: where = ' AND '.join( where_format(k, settings.INFLUXDB_CONDITION_OVERRIDES.get( k, v)) for k, v in self.criteria.items()) if self.group_by: if isinstance(self.group_by, (list, tuple)): group_by = ', '.join(ident(x) for x in self.group_by) else: group_by = ident(self.group_by) if self.timeframe: tf = USER_TIMEFRAMES.get( self.req.GET.get('timeframe', self.timeframe) if not self.force_timeframe else self.timeframe, lambda tz: None)(tz) if tf: if where: where += ' AND ' where += tf if self.interval_val: if group_by: group_by += ', ' group_by += INTERVALS[self.interval_val] % ( -1 * self._get_tz_offset().total_seconds() // 3600) query = 'SELECT %s FROM %s' % (select, ident(self.event_type)) if where: query += ' WHERE %s' % where if group_by: query += ' GROUP BY %s' % group_by query += ';' if settings.DEBUG: print(query) self.res = get_client().query(query, database=self.db) return self
def upgrade(req): us = UserSettings.get_from_user(req.user) customer = us.get_stripe_customer() ctx = { 'active_coupon': req.session.get('coupon'), 'coupon_applied': 'coupon_applied' in req.GET, 'coupon_invalid': 'coupon_invalid' in req.GET, 'coupon_unavailable': 'coupon_unavailable' in req.GET, 'error': req.GET.get('error'), 'stripe_customer': customer, 'success': 'success' in req.GET, } return _pmrender(req, 'payments/main.html', ctx)
def dashboard(req): ctx = { 'success': req.GET.get('success'), 'error': req.GET.get('error'), } us = UserSettings.get_from_user(req.user) if us.coupon_code: try: ctx['coupon'] = stripe.Coupon.retrieve(us.coupon_code) except Exception as e: if settings.DEBUG: raise e return _pmrender(req, 'dashboard/dashboard.html', ctx)
def dashboard(req): ctx = { 'success': req.GET.get('success'), 'error': req.GET.get('error'), } us = UserSettings.get_from_user(req.user) if us.coupon_code: try: ctx['coupon'] = stripe.Coupon.retrieve(us.coupon_code) except Exception as e: if settings.DEBUG: raise e return _pmrender(req, 'dashboard/dashboard.html', ctx)
def signup(req): if not req.user.is_anonymous(): return redirect('dashboard') if not req.POST: return _pmrender(req, 'signup/main.html', {'email': req.GET.get('email', '')}) error = None if not _validate_recaptcha(req): error = ugettext('Your humanity was not verified') elif not req.POST.get('email'): error = ugettext('Missing email address') elif not req.POST.get('password'): error = ugettext('Come on, you need a password') elif len(req.POST.get('password')) < 8: error = ugettext( 'Your password needs to be at least 8 characters long') elif User.objects.filter(email=req.POST.get('email')).count(): error = ugettext( 'That email address is already associated with an account') if error: return _pmrender(req, 'signup/main.html', { 'error': error, 'email': req.POST.get('email'), }) try: u = User.objects.create_user( str(uuid.uuid4())[:30], req.POST.get('email'), req.POST.get('password')) u.save() except Exception as e: return _pmrender(req, 'signup/main.html', { 'error': str(e), 'email': req.POST.get('email'), }) try: us = UserSettings.get_from_user(u) us.tz_offset = req.POST.get('timezone') us.save() except Exception: pass # whatever. return redirect(reverse('login') + '?signup_success=true')
def wrapper(*args, **kwargs): req = args[0] if not req.user: return HttpResponseForbidden() pod = get_podcast(req, req.GET.get("podcast")) uset = UserSettings.get_from_user(pod.owner) if not plans.minimum(uset.plan, minimum_plan) and not req.user.is_staff: return HttpResponseForbidden() resp = view(req, pod, *args[1:], **kwargs) if not isinstance(resp, (dict, list, bool, int, float) + StringTypes) and resp is not None: # Handle HttpResponse/HttpResponseBadRequest/etc return resp return JsonResponse(resp, safe=False)
def _get_podcast(req, **kwargs): podcast = get_object_or_404(Podcast, **kwargs) us = UserSettings.get_from_user(podcast.owner) if not minimum(us.plan, FEATURE_MIN_NOTIFICATIONS): raise HttpResponseForbidden() if req.user.is_staff: return podcast if req.user == podcast.owner: return podcast pods = Network.objects.filter(deactivated=False, members__in=[req.user], podcast__in=[podcast]) if not pods.count(): raise Http404() return podcast
def upgrade_set_plan(req): new_plan = req.POST.get('plan') if new_plan not in AVAILABLE_PLANS: return redirect('upgrade') new_plan_val = AVAILABLE_PLANS[new_plan] us = UserSettings.get_from_user(req.user) result = us.set_plan(new_plan_val, req.session.get('coupon')) if not result: return redirect('upgrade') elif result == 'card_error': return redirect(reverse('upgrade') + '?error=card') else: req.session['coupon'] = None return redirect(reverse('upgrade') + '?success')
def wrapper(*args, **kwargs): req = args[0] if not req.user: return HttpResponseForbidden() pod = get_podcast(req, req.GET.get('podcast')) uset = UserSettings.get_from_user(pod.owner) if (not plans.minimum(uset.plan, minimum_plan) and not req.user.is_staff): return HttpResponseForbidden() resp = view(req, pod, *args[1:], **kwargs) if not isinstance(resp, (dict, list, bool, int, float) + StringTypes) and resp is not None: # Handle HttpResponse/HttpResponseBadRequest/etc return resp return JsonResponse(resp, safe=False)
def new_podcast(req): uset = UserSettings.get_from_user(req.user) if payment_plans.has_reached_podcast_limit(uset): return _pmrender(req, 'dashboard/podcast/page_new_upgrade.html') ctx = { 'LOCALES': constants.locales, 'reached_podcast_limit': payment_plans.has_reached_podcast_limit(uset) } if not req.POST: return _pmrender(req, 'dashboard/podcast/page_new.html', ctx) # Basic validation if (not req.POST.get('slug') or not req.POST.get('name')): ctx.update(default=req.POST, error=True) return _pmrender(req, 'dashboard/podcast/page_new.html', ctx) try: pod = Podcast( slug=req.POST.get('slug'), name=req.POST.get('name'), subtitle=req.POST.get('subtitle'), cover_image=signer.unsign(req.POST.get('image-url')), description=req.POST.get('description'), is_explicit=req.POST.get('is_explicit', 'false') == 'true', homepage=req.POST.get('homepage'), language=req.POST.get('language'), copyright=req.POST.get('copyright'), author_name=req.POST.get('author_name'), owner=req.user) pod.clean() pod.save() except Exception as e: ctx.update(default=req.POST, error=True) return _pmrender(req, 'dashboard/podcast/page_new.html', ctx) try: pod.set_category_list(req.POST.get('categories')) except Exception: pass return redirect('podcast_dashboard', podcast_slug=pod.slug)
def set_tip_cashout(req): try: dob = iso8601.parse_date(req.POST.get('dob')) except Exception: return {'success': False, 'error': 'invalid dob'} forwarded_for = req.META.get('HTTP_X_FORWARDED_FOR') if forwarded_for: ip = forwarded_for.split(',')[0] else: ip = req.META.get('REMOTE_ADDR') legal_entity = { 'address': { 'city': req.POST.get('addressCity'), 'state': req.POST.get('addressState'), 'postal_code': req.POST.get('addressZip'), 'line1': req.POST.get('addressStreet'), 'line2': req.POST.get('addressSecond'), }, 'ssn_last_4': req.POST.get('ssnLastFour'), 'dob': { 'day': dob.day, 'month': dob.month, 'year': dob.year, }, 'first_name': req.POST.get('firstName'), 'last_name': req.POST.get('lastName'), 'type': 'individual', } us = UserSettings.get_from_user(req.user) account = us.get_stripe_managed_account() if account: account.external_account = req.POST.get('token') for key in legal_entity: setattr(account.legal_entity, key, legal_entity[key]) account.save() else: us.create_stripe_managed_account(req.POST.get('token'), ip, legal_entity) return {'success': True}
def get_episodes(req): pod_slug = req.GET.get('podcast') network = req.GET.get('network_id') start_date = req.GET.get('start_date') if not pod_slug and not network: return [] pods = set() if pod_slug: pods.add(get_podcast(req, pod_slug)) if network: net = get_object_or_404(Network, id=network, members__in=[req.user]) pods = pods | set(net.podcast_set.all()) if not pods: return [] query = PodcastEpisode.objects.filter( podcast__in=list(pods), publish__lt=datetime.datetime.now(), awaiting_import=False ).select_related('podcast') if start_date: try: parsed_date = datetime.datetime.strptime(start_date, '%Y-%m-%dT%H:%M:%S') except ValueError: raise Http404() query = query.filter(publish__gte=parsed_date) uset = UserSettings.get_from_user(req.user) tz_delta = uset.get_tz_delta() return [ {'id': ep.id, 'title': ep.title, 'podcastSlug': ep.podcast.slug, 'publish': (ep.publish + tz_delta).strftime('%Y-%m-%dT%H:%M:%S')} for ep in sorted(query, key=lambda x: x.publish) ]
def get_available_flair_flags(self, flatten=False): us = UserSettings.get_from_user(self.owner) plan = us.plan flags = [] if payment_plans.minimum(plan, payment_plans.PLAN_STARTER): # This is inside a conditional because it's forced on for free # users. flags.append(FLAIR_POWERED_BY) if payment_plans.minimum(plan, payment_plans.FEATURE_MIN_COMMENT_BOX): flags.append(FLAIR_FEEDBACK) if us.stripe_payout_managed_account: flags.append(FLAIR_TIP_JAR) if payment_plans.minimum( plan, payment_plans.FEATURE_MIN_SITES) and self.get_site(): flags.append(FLAIR_SITE_LINK) if flatten: return flags else: return [(f, FLAIR_FLAGS_MAP[f]) for f in flags]