def generate_media_block(self, default_size=100): # Make a thumbnail for the first photo or video media_block = '' link_kwargs = None photo_annotations = get_photo_annotations(self.post_a.get('annotations', [])) video_annotations = get_video_annotations(self.post_a.get('annotations', [])) if photo_annotations: icon_class = 'icon-zoom-in' value = photo_annotations[0]['value'] link_kwargs = { 'href': value.get('embeddable_url', smart_reverse(self.request, 'photo', args=[self.post_a.user.username, self.post_a.id, 1])), } # keep the :// here photos_domain = '://photos.app.net' if photos_domain not in link_kwargs['href']: link_kwargs['target'] = '_blank' link_kwargs['rel'] = 'nofollow' else: link_kwargs['data-pjax-url'] = smart_reverse(self.request, 'pau.views.alpha.photo', args=[self.post_a.user.username, self.post_a.id, 1], force_qualified=True) elif video_annotations: icon_class = 'icon-play-circle' value = video_annotations[0]['value'] embeddable_url = value.get('embeddable_url') if embeddable_url: link_kwargs = { 'href': embeddable_url, 'target': '_blank', 'rel': 'nofollow' } if link_kwargs: thumbnail_width = value['thumbnail_width'] thumbnail_height = value['thumbnail_height'] thumbnail_url = value.get('thumbnail_url_secure', value['thumbnail_url']) try: max_width = int(self.request.GET.get('max_width', default_size)) max_height = int(self.request.GET.get('max_height', default_size)) except: max_width = max_height = default_size include_zoom = bool(self.request.GET.get('include_zoom', True)) display_width, display_height = map(str, fit_to_box(thumbnail_width, thumbnail_height, max_width, max_height)) media_block = [ html.div(class_='post-media', *[ html.a(class_="shadow-overlay", data=self.click_data, *[ html.i(class_=icon_class) if include_zoom else '' ], **link_kwargs), html.div(class_='inner-shadow-overlay'), html.img(src=thumbnail_url, width=display_width, height=display_height) ]) ] return media_block
def populate_context(self, request, username=None, post_id=None, photo_id=None, *args, **kwargs): super(PauPhotoView, self).populate_context(request, *args, **kwargs) try: post_id = int(post_id) photo_id = int(photo_id) except: raise Http404() post_api_obj = bridge.get_post(request, post_id) if not verify_post(username, post_api_obj): raise Http404() photo_annotations = get_photo_annotations(post_api_obj.get('annotations', [])) num_photos = len(photo_annotations) # N.B. photo_id is 1-based if photo_id < 1 or photo_id > num_photos: raise Http404() photo_annotation = photo_annotations[photo_id - 1] value = photo_annotation['value'] width = value['width'] height = value['height'] url = value.get('url_secure', value['url']) next_photo_url = None prev_photo_url = None if photo_id > 1: prev_photo_url = smart_reverse(request, 'photo', args=[username, post_id, photo_id - 1]) if photo_id < num_photos: next_photo_url = smart_reverse(request, 'photo', args=[username, post_id, photo_id + 1]) post_presenter = PhotoPostPresenter.from_item(request, post_api_obj) self.view_ctx['page_title'] = u'App.net | %s photo: %s' % (post_api_obj.user.username, truncate(post_api_obj.get('text', ''), 50)) self.view_ctx['page_description'] = truncate(post_api_obj.get('text', self.view_ctx['page_title']), 100) self.view_ctx.update_ctx({ 'image_url': url, 'image_width': width, 'image_height': height, 'next_photo_url': next_photo_url, 'prev_photo_url': prev_photo_url, 'post_presenter': post_presenter, 'post_url': smart_reverse(request, 'post_detail_view', args=[username, post_id]), 'post_api_obj': post_api_obj, 'oembed_url': oembed_url(request.build_absolute_uri()), '__js_page_load_hooks': ['photo.init'] })
def generate_html(self): avatar_size = '30s' max_num_faces = 5 li_fn = lambda user: html.li(class_='yui3-u', title=user.username, *[ html.a(href=smart_reverse(self.request, 'user_detail_view', args=[user.username]), class_='avatar facepile-size', style={'background-image': 'url(%s)' % user.avatar_image.get(avatar_size, '')}) ]) tree = html.ul( *[li_fn(user) for user in self.starred_by[:max_num_faces]] ) return tree
def generate_obj_link(self): obj = self.interaction.objects[0] if self.interaction.objects else None if self.action == 'follow': return 'you' elif self.action in ('star', 'reply', 'repost'): return html.a(href=smart_reverse(self.request, 'post_detail_view', kwargs={'username': self.request.user.username, 'post_id': obj['id']}), *['your post']) elif self.action in ('broadcast_create', 'broadcast_subscribe', 'broadcast_unsubscribe'): return html.a(href=obj.canonical_url, *[obj.title]) elif self.action == 'welcome': return 'App.net' return ''
def from_item(cls, request, post_a, show_deleted=False, single_post=False, show_stream_marker=False, reply_link_format='to_self', in_conversation=False, click_data=None, *args, **kwargs): presenter = cls(*args, **kwargs) presenter.request = request presenter.post_a = post_a presenter.repost = None if hasattr(post_a, 'repost_of') and post_a.repost_of: presenter.repost = post_a # Render the original post not the repost presenter.post_a = post_a = post_a.repost_of if post_a.user: presenter.user_detail_url = smart_reverse( request, 'user_detail_view', args=[post_a.user.username]) presenter.post_detail_url = smart_reverse(request, 'post_detail_view', kwargs={ 'username': post_a.user.username, 'post_id': str(post_a.id) }) presenter.show_deleted = show_deleted presenter.single_post = single_post presenter.reply_link_format = reply_link_format presenter.in_conversation = in_conversation presenter.is_authenticated = request.user.is_authenticated() presenter.show_stream_marker = request.user.is_authenticated( ) and request.user.preferences.use_stream_markers and show_stream_marker presenter.click_data = click_data or {} return presenter
def action_redirect_to_signup(self, request, *args, **kwargs): user_id = request.POST.get('user_id') if not user_id: raise Http404() extra_params = { 'action': 'follow', 'user_id': user_id, } request.session['next_blob'] = extra_params return HttpResponseRedirect(smart_reverse(request, 'login', url_params={'next': request.build_absolute_uri()}))
def from_item(cls, request, post_a, show_deleted=False, single_post=False, show_stream_marker=False, reply_link_format='to_self', in_conversation=False, click_data=None, *args, **kwargs): presenter = cls(*args, **kwargs) presenter.request = request presenter.post_a = post_a presenter.repost = None if hasattr(post_a, 'repost_of') and post_a.repost_of: presenter.repost = post_a # Render the original post not the repost presenter.post_a = post_a = post_a.repost_of if post_a.user: presenter.user_detail_url = smart_reverse(request, 'user_detail_view', args=[post_a.user.username]) presenter.post_detail_url = smart_reverse(request, 'post_detail_view', kwargs={'username': post_a.user.username, 'post_id': str(post_a.id)}) presenter.show_deleted = show_deleted presenter.single_post = single_post presenter.reply_link_format = reply_link_format presenter.in_conversation = in_conversation presenter.is_authenticated = request.user.is_authenticated() presenter.show_stream_marker = request.user.is_authenticated() and request.user.preferences.use_stream_markers and show_stream_marker presenter.click_data = click_data or {} return presenter
def action_redirect_to_signup(self, request, *args, **kwargs): user_id = request.POST.get('user_id') if not user_id: raise Http404() extra_params = { 'action': 'follow', 'user_id': user_id, } request.session['next_blob'] = extra_params return HttpResponseRedirect( smart_reverse(request, 'login', url_params={'next': request.build_absolute_uri()}))
def well_known_webfinger(request): resource = request.GET.get('resource') if not resource: raise Http404() fake_email = resource.lower().replace('acct:', '') username, domain = fake_email.split('@', 1) current_domain = 'alpha.%s' % (settings.PARENT_HOST) if domain != current_domain: raise Http404() user = bridge.get_user_by_username(request, username) if not user: raise Http404() def link(rel, href, _type=None): link = dict(rel=rel, href=href) if _type: link['type'] = _type return link user_profile = smart_reverse(request, 'user_detail_view', args=[username], force_qualified=True) activitystream = 'https://api.%s/users/@%s/activitystream' % ( settings.PARENT_HOST, username) resp = json.dumps({ "subject": 'acct:%s@%s' % (username, current_domain), "aliases": [ user_profile, ], "links": [ link(rel='http://webfinger.net/rel/profile-page', href=user_profile, _type='text/html'), link(rel='http://activitystrea.ms/specs/json/1.0/', href=activitystream) ] }) return HttpResponse(resp, content_type='application/json')
def populate_context(self, request, *args, **kwargs): super(PauMMLActionView, self).populate_context(request, *args, **kwargs) self.view_ctx.update_ctx({ '__js_page_load_hooks': ['utils.handle_resize', 'init_pau', 'init_post_delete', 'init_mute_user', 'init_post_report', 'init_star_post', 'init_repost', 'pau.init_fixed_nav'], '__js_api_options': { 'api_base_url': smart_reverse(request, 'omo_api_proxy'), }, '__js_canvas_mode': 'pau', '__js_subscribe_url': 'https://account.app.net/upgrade/', '__js_upgrade_storage_url': 'https://account.app.net/settings/upgrade/storage/', 'selected_nav_page': self.selected_nav_page, 'explore_streams': bridge.list_explore_streams(request), 'report_post_form': ReportPostForm(), }) self.view_ctx.forms = []
def populate_context(self, request, *args, **kwargs): super(PauMMLActionView, self).populate_context(request, *args, **kwargs) self.view_ctx.update_ctx({ '__js_page_load_hooks': ['utils.handle_resize', 'init_pau', 'init_post_delete', 'init_mute_user', 'init_post_report', 'init_star_post', 'init_repost', 'pau.init_fixed_nav'], '__js_api_options': { 'api_base_url': smart_reverse(request, 'omo_api_proxy'), }, '__js_canvas_mode': 'pau', '__js_subscribe_url': 'https://account.app.net/upgrade/', '__js_upgrade_storage_url': 'https://account.app.net/settings/upgrade/storage/', 'selected_nav_page': self.selected_nav_page, 'explore_streams': bridge.list_explore_streams(request), 'report_post_form': ReportPostForm(), }) self.view_ctx.forms = []
def generate_html(self): avatar_size = '30s' max_num_faces = 5 li_fn = lambda user: html.li( class_='yui3-u', title=user.username, *[ html.a(href=smart_reverse( self.request, 'user_detail_view', args=[user.username]), class_='avatar facepile-size', style={ 'background-image': 'url(%s)' % user.avatar_image.get(avatar_size, '') }) ]) tree = html.ul( *[li_fn(user) for user in self.starred_by[:max_num_faces]]) return tree
def generate_obj_link(self): obj = self.interaction.objects[0] if self.interaction.objects else None if self.action == 'follow': return 'you' elif self.action in ('star', 'reply', 'repost'): return html.a(href=smart_reverse(self.request, 'post_detail_view', kwargs={ 'username': self.request.user.username, 'post_id': obj['id'] }), *['your post']) elif self.action in ('broadcast_create', 'broadcast_subscribe', 'broadcast_unsubscribe'): return html.a(href=obj.canonical_url, *[obj.title]) elif self.action == 'welcome': return 'App.net' return ''
def from_data(cls, request, is_following, target_id, target_username, target_description_dict, target_avatar, button_classes=None, is_onboarding=False, click_data=None, show_follow_buttons=True, *args, **kwargs): presenter = cls() presenter.request = request presenter.is_following = is_following presenter.target_id = target_id presenter.target_description = target_description_dict presenter.target_username = target_username presenter.target_avatar = target_avatar presenter.button_classes = button_classes presenter.is_onboarding = is_onboarding presenter.click_data = click_data presenter.show_follow_buttons = show_follow_buttons presenter.target_user_detail_url = smart_reverse( request, 'user_detail_view', args=[presenter.target_username] ) return presenter
def well_known_webfinger(request): resource = request.GET.get('resource') if not resource: raise Http404() fake_email = resource.lower().replace('acct:', '') username, domain = fake_email.split('@', 1) current_domain = 'alpha.%s' % (settings.PARENT_HOST) if domain != current_domain: raise Http404() user = bridge.get_user_by_username(request, username) if not user: raise Http404() def link(rel, href, _type=None): link = dict(rel=rel, href=href) if _type: link['type'] = _type return link user_profile = smart_reverse(request, 'user_detail_view', args=[username], force_qualified=True) activitystream = 'https://api.%s/users/@%s/activitystream' % (settings.PARENT_HOST, username) resp = json.dumps({ "subject": 'acct:%s@%s' % (username, current_domain), "aliases": [ user_profile, ], "links": [ link(rel='http://webfinger.net/rel/profile-page', href=user_profile, _type='text/html'), link(rel='http://activitystrea.ms/specs/json/1.0/', href=activitystream) ] }) return HttpResponse(resp, content_type='application/json')
def generate_media_block(self, default_size=100): # Make a thumbnail for the first photo or video media_block = '' link_kwargs = None photo_annotations = get_photo_annotations( self.post_a.get('annotations', [])) video_annotations = get_video_annotations( self.post_a.get('annotations', [])) if photo_annotations: icon_class = 'icon-zoom-in' value = photo_annotations[0]['value'] link_kwargs = { 'href': value.get( 'embeddable_url', smart_reverse( self.request, 'photo', args=[self.post_a.user.username, self.post_a.id, 1])), } # keep the :// here photos_domain = '://photos.app.net' if photos_domain not in link_kwargs['href']: link_kwargs['target'] = '_blank' link_kwargs['rel'] = 'nofollow' else: link_kwargs['data-pjax-url'] = smart_reverse( self.request, 'pau.views.alpha.photo', args=[self.post_a.user.username, self.post_a.id, 1], force_qualified=True) elif video_annotations: icon_class = 'icon-play-circle' value = video_annotations[0]['value'] embeddable_url = value.get('embeddable_url') if embeddable_url: link_kwargs = { 'href': embeddable_url, 'target': '_blank', 'rel': 'nofollow' } if link_kwargs: thumbnail_width = value['thumbnail_width'] thumbnail_height = value['thumbnail_height'] thumbnail_url = value.get('thumbnail_url_secure', value['thumbnail_url']) try: max_width = int(self.request.GET.get('max_width', default_size)) max_height = int( self.request.GET.get('max_height', default_size)) except: max_width = max_height = default_size include_zoom = bool(self.request.GET.get('include_zoom', True)) display_width, display_height = map( str, fit_to_box(thumbnail_width, thumbnail_height, max_width, max_height)) media_block = [ html.div(class_='post-media', *[ html.a(class_="shadow-overlay", data=self.click_data, *[ html.i(class_=icon_class) if include_zoom else '' ], **link_kwargs), html.div(class_='inner-shadow-overlay'), html.img(src=thumbnail_url, width=display_width, height=display_height) ]) ] return media_block
def build_tree_from_text_entity_pack( request, text_entity_pack, itemscope='https://join.app.net/schemas/Post', convert_new_lines=False): # adapted from omo models TextEntityPack.html def entity_text(e): return text_entity_pack['text'][e['pos']:e['pos'] + e['len']] mention_builder = lambda m: html.a(itemprop='mention', data={ 'mention-name': m['name'], 'mention-id': m['id'] }, href=smart_reverse(request, 'user_detail_view', args=[m['name']], force_qualified=True ), *[entity_text(m)]) hashtag_builder = lambda h: html.a( itemprop='hashtag', data={'hashtag-name': h['name']}, href=smart_reverse( request, 'hashtags', args=[h['name']], force_qualified=True), *[entity_text(h)]) link_builder = lambda l: html.a( href=l['url'], target="_blank", rel='nofollow', *[entity_text(l)]) # map starting position, length of entity placeholder to the replacement html entity_map = {} for entity_key, builder in [('mentions', mention_builder), ('hashtags', hashtag_builder), ('links', link_builder)]: for entity in text_entity_pack.get('entities', {}).get(entity_key, []): try: entity_map[(entity['pos'], entity['len'])] = builder(entity) except NoReverseMatch: logger.warning( 'Could not build link for entity=%s in Pau path %s.', entity.get('name'), request.path) # replace strings with html html_pieces = [] text_idx = 0 # our current place in the original text string for entity_start, entity_len in sorted(entity_map.keys()): if text_idx != entity_start: # if our current place isn't the start of an entity, bring in text until the next entity html_pieces.append( text_entity_pack.get('text', "")[text_idx:entity_start]) # pull out the entity html entity_html = entity_map[(entity_start, entity_len)] html_pieces.append(entity_html) # move past the entity we just added text_idx = entity_start + entity_len # clean up any remaining text html_pieces.append(text_entity_pack.get('text', "")[text_idx:]) if convert_new_lines: new_html_pieces = [] for piece in html_pieces: if isinstance(piece, basestring) and '\n' in piece: new_html_pieces += list( intersperse(html.br(), piece.split('\n'))) else: new_html_pieces.append(piece) html_pieces = new_html_pieces # TODO: link to schema return html.span(itemscope=itemscope, *html_pieces)
def generate_post_footer(self): if self.post_a.get('is_deleted'): return '' footer_top_row = [] # repost # N.B. if you change this html (reposts), make sure to change the ajax handler for repost, # which duplicates this html to provide client-side feedback viewer_has_reposted = self.post_a.get('you_reposted') if self.repost or viewer_has_reposted: # yes, there is a priority order of reposter info - viewer trumps everyone else if viewer_has_reposted: reposter_username = self.request.user.username reposter_display = 'you' elif self.repost: reposter_username = self.repost.user.username reposter_display = '@' + reposter_username footer_top_row.append( html.div(class_='post-reposted-by yui3-u', *[ html.span(class_='reposted-by-text', *[ html.i(class_='icon-repost'), html.span(' Reposted by ', *[ html.a(href=smart_reverse(self.request, 'user_detail_view', args=[reposter_username]), data=self.click_data, *[reposter_display]), ]) ]) ]) ) # place place_annotation = get_place_annotation(self.post_a.get('annotations', [])) if place_annotation: if place_annotation['value'].get('address'): place_pretty = u'%s \u2014 %s' % (place_annotation['value']['name'], place_annotation['value']['address']) else: place_pretty = u'%s' % place_annotation['value']['name'] tags_html = [] if place_annotation.get('type') == 'net.app.core.checkin': factual_url = urljoin('http://factual.com/', place_annotation['value']['factual_id']) tags_html.append(html.meta(name="factual", content=factual_url)) tags_html.append(html.span(class_='posted-from-text', *[ html.i(class_='icon-pushpin'), html.span(' at %s' % place_pretty), ])) footer_top_row.append(html.div(class_='post-posted-from-place yui3-u', *tags_html)) footer_bottom_row = [] # timestamp timezone_str = self.request.user.adn_user.timezone if self.is_authenticated else 'America/Los_Angeles' viewers_timezone = pytz.timezone(timezone_str) non_relative_timestamp = pytz.utc.localize(self.post_a.created_at).astimezone(viewers_timezone) datetime_formatted = non_relative_timestamp.strftime("%I:%M %p - %d %b %Y") footer_bottom_row.append(html.li( html.a(href=self.post_detail_url, data=self.click_data, class_='timestamp u-url', title=datetime_formatted, *[ html.time(class_='dt-published', datetime=datetime_formatted, *[ html.i(class_='icon-time yui3-u'), " " + naturaldate(self.post_a.created_at), ]) ])), ) is_reply = hasattr(self.post_a, 'reply_to') and self.post_a.reply_to reply_to_hash = "#" + str(self.post_a.reply_to) if is_reply else '' # conversation active? if is_reply or (self.post_a.num_replies > 0 and not self.single_post): footer_bottom_row.append( html.li(class_='in-reply-to yui3-u', *[ html.a(href=self.post_detail_url + reply_to_hash, data=self.click_data, title='In Reply To...', *[ html.i(class_='icon-comments', **{'aria-label': 'In Reply To...'}) ]) ]) ) # reply if self.is_authenticated and self.show_reply_button: footer_bottom_row.append(self.reply_button()) if self.show_stream_marker: data = { 'set-stream-marker': '' } footer_bottom_row.append( html.li(class_='show-on-hover yui3-u stream-marker-button', *[ html.a(href='#', data=data, *[ html.i(class_='icon-bookmark'), "" ]) ]) ) if self.post_a.source and self.show_via_attribution: source_link = getattr(self.post_a.source, 'link', None) source_name = getattr(self.post_a.source, 'name', None) if source_link and source_name: footer_bottom_row.append( html.li(class_='show-on-hover post-source yui3-u', *[ html.a(href=self.post_a.source.link, rel='nofollow', target='_blank', *[ html.i(class_='icon-share'), ' via ' + source_name ]) ]) ) # crosspost annotations = self.post_a.get('annotations', []) cp_url = None for a in annotations: annotation_type = a.get('type') if annotation_type == "net.app.core.crosspost": cp_url = a.get('value', {}).get('canonical_url') if cp_url and not re.match('^https?://', cp_url, re.IGNORECASE): cp_url = "http://" + cp_url if cp_url: cp_url_display = urlparse(cp_url).netloc if cp_url_display.startswith('www.'): cp_url_display = cp_url_display[4:] footer_bottom_row.append( html.li(class_='show-on-hover crossposted-from yui3-u', *[ html.a(href=cp_url, target='_blank', *[ html.i(class_='icon-random'), ' from ' + cp_url_display ]) ]) ) # report this post to app.net if self.show_report_button: if self.is_authenticated and self.request.user.adn_user.id != self.post_a.user.id: footer_bottom_row.append( html.li(class_='show-on-hover last pull-right yui3-u', *[ html.a(href='#report', data={'post-report': ''}, *[ html.i(class_='icon-flag'), html.span(class_='t-yui3-u-none m-yui3-u-none', *[' Report']), ]) ]) ) # mute this user--it's not really an if/else with the delete case so I'm not combining the conditions if self.show_mute_button: if self.is_authenticated and self.request.user.adn_user.id != self.post_a.user.id and not self.post_a.user.you_muted: footer_bottom_row.append( html.li(class_='show-on-hover pull-right yui3-u', *[ html.a(href='#mute-user', data={'post-mute-user': ''}, *[ html.i(class_='icon-minus-sign'), html.span(class_='t-yui3-u-none m-yui3-u-none', *[' Mute user']), ]) ]) ) # delete this post if self.show_delete_button: if self.is_authenticated and self.request.user.adn_user.id == self.post_a.user.id: footer_bottom_row.append( html.li(class_='show-on-hover last pull-right yui3-u', *[ html.a(href='#delete', data={'post-delete': ''}, *[ html.i(class_='icon-remove'), html.span(class_='t-yui3-u-none m-yui3-u-none', *[' Delete']), ]) ]) ) tree = html.div(class_='post-footer', *[ html.ul(class_='ul-horizontal unstyled footer-top', *footer_top_row), html.ul(class_='ul-horizontal unstyled footer-bottom', *footer_bottom_row) ]) return tree
def handle_not_authorized(self, request, *args, **kwargs): return HttpResponseRedirect(smart_reverse(request, 'login', url_params={'next': self.next_url(request)}))
def adjust_url(stream): stream['url'] = smart_reverse(request, 'explore', args=[stream['slug']]) return stream
def adjust_url(stream): stream["url"] = smart_reverse(request, "explore", args=[stream["slug"]]) return stream
def populate_context(self, request, username=None, post_id=None, photo_id=None, *args, **kwargs): super(PauPhotoView, self).populate_context(request, *args, **kwargs) try: post_id = int(post_id) photo_id = int(photo_id) except: raise Http404() post_api_obj = bridge.get_post(request, post_id) if not verify_post(username, post_api_obj): raise Http404() photo_annotations = get_photo_annotations( post_api_obj.get('annotations', [])) num_photos = len(photo_annotations) # N.B. photo_id is 1-based if photo_id < 1 or photo_id > num_photos: raise Http404() photo_annotation = photo_annotations[photo_id - 1] value = photo_annotation['value'] width = value['width'] height = value['height'] url = value.get('url_secure', value['url']) next_photo_url = None prev_photo_url = None if photo_id > 1: prev_photo_url = smart_reverse( request, 'photo', args=[username, post_id, photo_id - 1]) if photo_id < num_photos: next_photo_url = smart_reverse( request, 'photo', args=[username, post_id, photo_id + 1]) post_presenter = PhotoPostPresenter.from_item(request, post_api_obj) self.view_ctx['page_title'] = u'App.net | %s photo: %s' % ( post_api_obj.user.username, truncate(post_api_obj.get('text', ''), 50)) self.view_ctx['page_description'] = truncate( post_api_obj.get('text', self.view_ctx['page_title']), 100) self.view_ctx.update_ctx({ 'image_url': url, 'image_width': width, 'image_height': height, 'next_photo_url': next_photo_url, 'prev_photo_url': prev_photo_url, 'post_presenter': post_presenter, 'post_url': smart_reverse(request, 'post_detail_view', args=[username, post_id]), 'post_api_obj': post_api_obj, 'oembed_url': oembed_url(request.build_absolute_uri()), '__js_page_load_hooks': ['photo.init'] })
def populate_context(self, request, *args, **kwargs): super(PauPostDetailView, self).populate_context(request, *args, **kwargs) post_id = kwargs.get('post_id') before_post_objs, target_post_api_obj, after_post_objs = bridge.get_conversation( request, post_id) if target_post_api_obj.user and kwargs.get( 'username') != target_post_api_obj.user.username: new_url = smart_reverse(request, 'post_detail_view', kwargs={ 'username': target_post_api_obj.user.username, 'post_id': str(target_post_api_obj.id) }) self.view_ctx.response = HttpResponsePermanentRedirect(new_url) return if target_post_api_obj.get('is_deleted') and target_post_api_obj.get( 'repost_of'): # Deleted reposts should actually go away, so this can 404. If a user got deleted, their reposts may be ghosts # instead of being deleted correctly raise Http404() if all((target_post_api_obj.get('is_deleted'), (target_post_api_obj.get('reply_to') is None), (target_post_api_obj.get('num_replies') == 0))): # render normally but 404 status code self.view_ctx.status_code = 404 original_post = target_post_api_obj.get('repost_of') if original_post: # This is the post detail of a repost post, all activity should be redirected to the # original post. new_url = smart_reverse(request, 'post_detail_view', kwargs={ 'username': original_post.user.username, 'post_id': str(original_post.id) }) self.view_ctx.response = HttpResponsePermanentRedirect(new_url) return owner = target_post_api_obj.user owner_id = owner['id'] if owner else None viewer_is_author = request.user.is_authenticated( ) and owner_id == request.user.adn_user.id if owner: name = target_post_api_obj.user.get( 'name') or target_post_api_obj.user.username self.view_ctx['page_title'] = u'%s: %s on App.net' % ( name, truncate(target_post_api_obj.get('text', ''), 50)) self.view_ctx['post_create_pre_text'] = u'@%s ' % ( target_post_api_obj['user']['username'] ) if not viewer_is_author else '' self.view_ctx['page_description'] = u'%s' % (truncate( target_post_api_obj.get('text', ''), 100)) else: # user of root post was deleted self.view_ctx['page_title'] = u'App.net' self.view_ctx['post_create_pre_text'] = '' self.view_ctx[ 'page_description'] = u'This App.net post has been deleted' self.view_ctx['oembed_url'] = oembed_url(request.build_absolute_uri()) self.view_ctx['post_a'] = target_post_api_obj self.view_ctx['main_post_inner'] = ChooseFeedPostPresenter.from_item( request, target_post_api_obj, show_deleted=True, single_post=True, in_conversation=True) self.view_ctx['before_post_presenters'] = [ ChooseFeedPostPresenter.from_item(request, post_api_obj, show_deleted=True, in_conversation=True) for post_api_obj in before_post_objs ] self.view_ctx['after_post_presenters'] = [ ChooseFeedPostPresenter.from_item(request, post_api_obj, show_deleted=True, in_conversation=True) for post_api_obj in after_post_objs ] self.view_ctx['num_stars'] = target_post_api_obj.num_stars self.view_ctx['num_reposts'] = target_post_api_obj.num_reposts self.view_ctx[ 'star_facepile_presenter'] = StarFacepilePresenter.from_data( request, target_post_api_obj) self.view_ctx['owner'] = owner self.view_ctx['num_replies'] = target_post_api_obj['num_replies'] self.view_ctx['__js_refresh_on_post_create'] = '1' self.view_ctx['__js_permit_attachments'] = True if before_post_objs: self.view_ctx['__js_post_id'] = post_id self.view_ctx['__js_page_load_hooks'] += [ 'init_follow', 'zoom_to_post', 'post_create.init_post_file_uploader' ] if request.user.is_authenticated(): self.view_ctx['__js_page_load_hooks'] += [ 'post_create.init', ] self.view_ctx['__js_viewer_username'] = request.user.username self.view_ctx['post_box_presenter'] = PostCreatePresenter.from_data( request, btn_action='Reply', reply_to=target_post_api_obj)
def action_logout(self, request, *args, **kwargs): auth_logout(request) return HttpResponseRedirect(smart_reverse(request, 'home'))
def generate_post_footer(self): if self.post_a.get('is_deleted'): return '' footer_top_row = [] # repost # N.B. if you change this html (reposts), make sure to change the ajax handler for repost, # which duplicates this html to provide client-side feedback viewer_has_reposted = self.post_a.get('you_reposted') if self.repost or viewer_has_reposted: # yes, there is a priority order of reposter info - viewer trumps everyone else if viewer_has_reposted: reposter_username = self.request.user.username reposter_display = 'you' elif self.repost: reposter_username = self.repost.user.username reposter_display = '@' + reposter_username footer_top_row.append( html.div(class_='post-reposted-by yui3-u', *[ html.span( class_='reposted-by-text', *[ html.i(class_='icon-repost'), html.span( ' Reposted by ', *[ html.a(href=smart_reverse( self.request, 'user_detail_view', args=[reposter_username]), data=self.click_data, *[reposter_display]), ]) ]) ])) # place place_annotation = get_place_annotation( self.post_a.get('annotations', [])) if place_annotation: if place_annotation['value'].get('address'): place_pretty = u'%s \u2014 %s' % ( place_annotation['value']['name'], place_annotation['value']['address']) else: place_pretty = u'%s' % place_annotation['value']['name'] tags_html = [] if place_annotation.get('type') == 'net.app.core.checkin': factual_url = urljoin('http://factual.com/', place_annotation['value']['factual_id']) tags_html.append(html.meta(name="factual", content=factual_url)) tags_html.append( html.span(class_='posted-from-text', *[ html.i(class_='icon-pushpin'), html.span(' at %s' % place_pretty), ])) footer_top_row.append( html.div(class_='post-posted-from-place yui3-u', *tags_html)) footer_bottom_row = [] # timestamp timezone_str = self.request.user.adn_user.timezone if self.is_authenticated else 'America/Los_Angeles' viewers_timezone = pytz.timezone(timezone_str) non_relative_timestamp = pytz.utc.localize( self.post_a.created_at).astimezone(viewers_timezone) datetime_formatted = non_relative_timestamp.strftime( "%I:%M %p - %d %b %Y") footer_bottom_row.append( html.li( html.a(href=self.post_detail_url, data=self.click_data, class_='timestamp u-url', title=datetime_formatted, *[ html.time(class_='dt-published', datetime=datetime_formatted, *[ html.i(class_='icon-time yui3-u'), " " + naturaldate(self.post_a.created_at), ]) ])), ) is_reply = hasattr(self.post_a, 'reply_to') and self.post_a.reply_to reply_to_hash = "#" + str(self.post_a.reply_to) if is_reply else '' # conversation active? if is_reply or (self.post_a.num_replies > 0 and not self.single_post): footer_bottom_row.append( html.li(class_='in-reply-to yui3-u', *[ html.a(href=self.post_detail_url + reply_to_hash, data=self.click_data, title='In Reply To...', *[ html.i( class_='icon-comments', **{'aria-label': 'In Reply To...'}) ]) ])) # reply if self.is_authenticated and self.show_reply_button: footer_bottom_row.append(self.reply_button()) if self.show_stream_marker: data = {'set-stream-marker': ''} footer_bottom_row.append( html.li(class_='show-on-hover yui3-u stream-marker-button', *[ html.a(href='#', data=data, *[html.i(class_='icon-bookmark'), ""]) ])) if self.post_a.source and self.show_via_attribution: source_link = getattr(self.post_a.source, 'link', None) source_name = getattr(self.post_a.source, 'name', None) if source_link and source_name: footer_bottom_row.append( html.li(class_='show-on-hover post-source yui3-u', *[ html.a(href=self.post_a.source.link, rel='nofollow', target='_blank', *[ html.i(class_='icon-share'), ' via ' + source_name ]) ])) # crosspost annotations = self.post_a.get('annotations', []) cp_url = None for a in annotations: annotation_type = a.get('type') if annotation_type == "net.app.core.crosspost": cp_url = a.get('value', {}).get('canonical_url') if cp_url and not re.match('^https?://', cp_url, re.IGNORECASE): cp_url = "http://" + cp_url if cp_url: cp_url_display = urlparse(cp_url).netloc if cp_url_display.startswith('www.'): cp_url_display = cp_url_display[4:] footer_bottom_row.append( html.li(class_='show-on-hover crossposted-from yui3-u', *[ html.a(href=cp_url, target='_blank', *[ html.i(class_='icon-random'), ' from ' + cp_url_display ]) ])) # report this post to app.net if self.show_report_button: if self.is_authenticated and self.request.user.adn_user.id != self.post_a.user.id: footer_bottom_row.append( html.li( class_='show-on-hover last pull-right yui3-u', *[ html.a( href='#report', data={'post-report': ''}, *[ html.i(class_='icon-flag'), html.span( class_='t-yui3-u-none m-yui3-u-none', *[' Report']), ]) ])) # mute this user--it's not really an if/else with the delete case so I'm not combining the conditions if self.show_mute_button: if self.is_authenticated and self.request.user.adn_user.id != self.post_a.user.id and not self.post_a.user.you_muted: footer_bottom_row.append( html.li( class_='show-on-hover pull-right yui3-u', *[ html.a( href='#mute-user', data={'post-mute-user': ''}, *[ html.i(class_='icon-minus-sign'), html.span( class_='t-yui3-u-none m-yui3-u-none', *[' Mute user']), ]) ])) # delete this post if self.show_delete_button: if self.is_authenticated and self.request.user.adn_user.id == self.post_a.user.id: footer_bottom_row.append( html.li( class_='show-on-hover last pull-right yui3-u', *[ html.a( href='#delete', data={'post-delete': ''}, *[ html.i(class_='icon-remove'), html.span( class_='t-yui3-u-none m-yui3-u-none', *[' Delete']), ]) ])) tree = html.div(class_='post-footer', *[ html.ul(class_='ul-horizontal unstyled footer-top', *footer_top_row), html.ul( class_='ul-horizontal unstyled footer-bottom', *footer_bottom_row) ]) return tree
def action_logout(self, request, *args, **kwargs): auth_logout(request) return HttpResponseRedirect(smart_reverse(request, "home"))
def populate_context(self, request, *args, **kwargs): super(PauPostDetailView, self).populate_context(request, *args, **kwargs) post_id = kwargs.get('post_id') before_post_objs, target_post_api_obj, after_post_objs = bridge.get_conversation(request, post_id) if target_post_api_obj.user and kwargs.get('username') != target_post_api_obj.user.username: new_url = smart_reverse(request, 'post_detail_view', kwargs={'username': target_post_api_obj.user.username, 'post_id': str(target_post_api_obj.id)}) self.view_ctx.response = HttpResponsePermanentRedirect(new_url) return if target_post_api_obj.get('is_deleted') and target_post_api_obj.get('repost_of'): # Deleted reposts should actually go away, so this can 404. If a user got deleted, their reposts may be ghosts # instead of being deleted correctly raise Http404() if all((target_post_api_obj.get('is_deleted'), (target_post_api_obj.get('reply_to') is None), (target_post_api_obj.get('num_replies') == 0))): # render normally but 404 status code self.view_ctx.status_code = 404 original_post = target_post_api_obj.get('repost_of') if original_post: # This is the post detail of a repost post, all activity should be redirected to the # original post. new_url = smart_reverse(request, 'post_detail_view', kwargs={'username': original_post.user.username, 'post_id': str(original_post.id)}) self.view_ctx.response = HttpResponsePermanentRedirect(new_url) return owner = target_post_api_obj.user owner_id = owner['id'] if owner else None viewer_is_author = request.user.is_authenticated() and owner_id == request.user.adn_user.id if owner: name = target_post_api_obj.user.get('name') or target_post_api_obj.user.username self.view_ctx['page_title'] = u'%s: %s on App.net' % (name, truncate(target_post_api_obj.get('text', ''), 50)) self.view_ctx['post_create_pre_text'] = u'@%s ' % (target_post_api_obj['user']['username']) if not viewer_is_author else '' self.view_ctx['page_description'] = u'%s' % (truncate(target_post_api_obj.get('text', ''), 100)) else: # user of root post was deleted self.view_ctx['page_title'] = u'App.net' self.view_ctx['post_create_pre_text'] = '' self.view_ctx['page_description'] = u'This App.net post has been deleted' self.view_ctx['oembed_url'] = oembed_url(request.build_absolute_uri()) self.view_ctx['post_a'] = target_post_api_obj self.view_ctx['main_post_inner'] = ChooseFeedPostPresenter.from_item(request, target_post_api_obj, show_deleted=True, single_post=True, in_conversation=True) self.view_ctx['before_post_presenters'] = [ ChooseFeedPostPresenter.from_item(request, post_api_obj, show_deleted=True, in_conversation=True) for post_api_obj in before_post_objs ] self.view_ctx['after_post_presenters'] = [ ChooseFeedPostPresenter.from_item(request, post_api_obj, show_deleted=True, in_conversation=True) for post_api_obj in after_post_objs ] self.view_ctx['num_stars'] = target_post_api_obj.num_stars self.view_ctx['num_reposts'] = target_post_api_obj.num_reposts self.view_ctx['star_facepile_presenter'] = StarFacepilePresenter.from_data(request, target_post_api_obj) self.view_ctx['owner'] = owner self.view_ctx['num_replies'] = target_post_api_obj['num_replies'] self.view_ctx['__js_refresh_on_post_create'] = '1' self.view_ctx['__js_permit_attachments'] = True if before_post_objs: self.view_ctx['__js_post_id'] = post_id self.view_ctx['__js_page_load_hooks'] += ['init_follow', 'zoom_to_post', 'post_create.init_post_file_uploader'] if request.user.is_authenticated(): self.view_ctx['__js_page_load_hooks'] += ['post_create.init', ] self.view_ctx['__js_viewer_username'] = request.user.username self.view_ctx['post_box_presenter'] = PostCreatePresenter.from_data(request, btn_action='Reply', reply_to=target_post_api_obj)
def _user_link(self, user): return smart_reverse(self.request, 'user_detail_view', args=[user['username']], force_qualified=True)
def _user_link(self, user): return smart_reverse(self.request, 'user_detail_view', args=[user['username']], force_qualified=True)
def build_tree_from_text_entity_pack(request, text_entity_pack, itemscope='https://join.app.net/schemas/Post', convert_new_lines=False): # adapted from omo models TextEntityPack.html def entity_text(e): return text_entity_pack['text'][e['pos']:e['pos'] + e['len']] mention_builder = lambda m: html.a( itemprop='mention', data={ 'mention-name': m['name'], 'mention-id': m['id'] }, href=smart_reverse(request, 'user_detail_view', args=[m['name']], force_qualified=True), *[entity_text(m)] ) hashtag_builder = lambda h: html.a( itemprop='hashtag', data={ 'hashtag-name': h['name'] }, href=smart_reverse(request, 'hashtags', args=[h['name']], force_qualified=True), *[entity_text(h)] ) link_builder = lambda l: html.a(href=l['url'], target="_blank", rel='nofollow', *[entity_text(l)]) # map starting position, length of entity placeholder to the replacement html entity_map = {} for entity_key, builder in [('mentions', mention_builder), ('hashtags', hashtag_builder), ('links', link_builder)]: for entity in text_entity_pack.get('entities', {}).get(entity_key, []): try: entity_map[(entity['pos'], entity['len'])] = builder(entity) except NoReverseMatch: logger.warning('Could not build link for entity=%s in Pau path %s.', entity.get('name'), request.path) # replace strings with html html_pieces = [] text_idx = 0 # our current place in the original text string for entity_start, entity_len in sorted(entity_map.keys()): if text_idx != entity_start: # if our current place isn't the start of an entity, bring in text until the next entity html_pieces.append(text_entity_pack.get('text', "")[text_idx:entity_start]) # pull out the entity html entity_html = entity_map[(entity_start, entity_len)] html_pieces.append(entity_html) # move past the entity we just added text_idx = entity_start + entity_len # clean up any remaining text html_pieces.append(text_entity_pack.get('text', "")[text_idx:]) if convert_new_lines: new_html_pieces = [] for piece in html_pieces: if isinstance(piece, basestring) and '\n' in piece: new_html_pieces += list(intersperse(html.br(), piece.split('\n'))) else: new_html_pieces.append(piece) html_pieces = new_html_pieces # TODO: link to schema return html.span(itemscope=itemscope, *html_pieces)
def adjust_url(stream): stream['url'] = smart_reverse(request, 'explore', args=[stream['slug']]) return stream