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 generate_html(self): classes = ['user-follow-container subpixel'] tree = html.div(class_=classes, *[ html.div(class_='content', *[ self.generate_body(), ]), ]) return tree
def generate_body(self): tree = html.div(class_='body follow-body', *[ html.div(class_='post-text user-description-block yui3-u-4-5 t-yui3-u-3-4 m-yui3-u-3-4', *[ html.div(class_='user-description-block-inner', *[ html.span(class_='username', *[self.email_user.email]), ]), ]), ]) return tree
def generate_in_reply_to_container(self): return html.div(class_='hide in-reply-to layout-like-p subpixel', data={'in-reply-to': 1}, *[ html.div(html.em(*['In Reply To:'])), html.div(class_='well-style reply-to', *[ html.a(href='#', class_='close relative space-5', data={'remove-reply': 1}, *[ html.i(class_='icon-remove') ]), html.div(class_='post-container subpixel', data={'post-copy': 1}) ]) ])
def generate_html(self): if self.post_a.get('is_deleted') and not self.show_deleted: return '' post_data = { 'post-id': self.post_a.id, # Should the post be remove from the page. # or should we insert [deleted] text 'post-remove-from-page': '0' if self.show_deleted else '1' } if self.post_a.user: post_data['post-author-username'] = self.post_a.user.username post_data['post-author-id'] = self.post_a.user.id else: post_data['post-author-deleted'] = '1' if self.repost: post_data['post-parent-id'] = self.repost.id classes = ['post-container', 'subpixel'] if not self.single_post and self.in_conversation: classes += ['p-in-reply-to', 'h-cite'] else: classes += ['h-entry'] if self.post_a.get('is_deleted'): classes.append('deleted') if self.hidden: classes.append('hide') avatar_size = 114 avatar_classes = ['avatar'] if self.single_post: classes.append('single-post-container') avatar_classes.append('large') avatar_size = 160 avatar_block = '' if self.post_a.user: avatar_url = append_query_string(self.post_a.user.avatar_image.url, params={'w': avatar_size, 'h': avatar_size}) avatar_block = html.div(class_='media', *[ html.a(class_=avatar_classes, data=self.click_data, style={'background-image': 'url(%s)' % avatar_url}, href=self.user_detail_url) ]) tree = html.div(class_=classes, data=post_data, name=str(self.post_a.id), *[ html.div(class_='content', *[ avatar_block, self.generate_post_header(), self.generate_post_body(), self.generate_post_footer() ]), ]) return tree
def generate_html(self): classes = ['user-follow-container subpixel'] if self.is_onboarding: classes.append('onboarding') tree = html.div(class_=classes, data={'user-id': self.target_id}, *[ html.div(class_='content', *[ html.div(class_='media', *[ self.user_avatar_presenter.generate_html(), ]), self.generate_body(), ]), ]) return tree
def generate_body(self): tree = html.div(class_='body follow-body', *[ html.div(class_='post-text user-description-block yui3-u-4-5 t-yui3-u-3-4 m-yui3-u-3-4', *[ html.div(class_='user-description-block-inner', *[ html.span(class_='username', *[self.username_presenter.generate_html()]), build_tree_from_text_entity_pack(self.request, self.target_description), ]), ]), html.div(class_='yui3-u-1-5 t-yui3-u-1-4 m-yui3-u-1-4 ta-right', *[ self.follow_button_presenter.generate_html() ]) if self.show_follow_buttons else '' ]) return tree
def generate_html(self): icon_url = None if self.target_avatar: icon_url = self.target_avatar return html.div(class_='autocomplete-block', *[ html.div(class_='content ellipsis', *[ html.div(class_='media', *[ html.img(src=icon_url, width='40', height='40') if icon_url else html.span() ]), html.span(*[self.target_username]), html.br(), html.small(class_='muted', *['Account']) ]) ])
def generate_post_body(self): # Main post text text_block = self.generate_post_body_text() # media annotation, eg. oEmbed photos media_block = self.generate_media_block() if media_block: tree = html.div(class_='body has-media', *[ html.div(class_='content', *[ html.div(class_='media', *media_block), text_block, ]) ]) else: tree = html.div(class_='body', *[ text_block, ]) return tree
def generate_post_body(self): # Main post text text_block = self.generate_post_body_text() # media annotation, eg. oEmbed photos media_block = self.generate_media_block(default_size=500) tree = html.div(class_='body has-media photo-under', *[text_block, media_block if media_block else '']) return tree
def generate_post_body_text(self): body_text = '[Post deleted]' if not self.post_a.get('is_deleted'): body_text = build_tree_from_text_entity_pack(self.request, self.post_a) text_block = html.div(class_='post-text', *[ html.span(class_='post-content e-content', *[ body_text, ]) ]) return text_block
def generate_post_body(self): # Main post text text_block = self.generate_post_body_text() # media annotation, eg. oEmbed photos media_block = self.generate_media_block(default_size=500) tree = html.div(class_='body has-media photo-over', *[ media_block if media_block else '', text_block, ]) return tree
def generate_html(self): if not self.request.user.is_authenticated(): return '' parts = [self.generate_textarea_container()] parts += [self.generate_in_reply_to_container()] if self.appended_post_url: parts += [self.generate_append_post_url()] parts += [self.generate_bottom_row()] return html.div(class_='well-plain well-elevated newpost', data={'message-create': 1}, *parts)
def generate_bottom_row(self): file_upload = file_upload_progress() success = html.span(class_='text-success hide', data={'success-text': 1}, *[self.success_message]) add_photo = html.button(class_='btn-attach-file file-related transition-color', data={'attach-btn': 1}, *[ html.i(class_='icon-picture'), u'Add photo\u2026' ]) char_count = self.generate_char_count() create_button = html.button(tabindex='2', data={'submit-button': 1}, class_='btn btn-primary %s-button btn-small disabled' % (self.btn_action.lower()), *[ self.btn_action ]) return html.grid(*[ html.div(class_='yui3-u-1-4 ta-left m-yui3-u-none', *[char_count]), html.div(class_='yui3-u-3-4 ta-right m-yui3-u-1', *[ file_upload, success, add_photo, create_button, ]) ])
def generate_html(self): truncated_description = ' '.join(self.target_description.get('text', '').split()) if len(truncated_description) > 85: truncated_description = truncated_description[:82] + " ..." classes = ['user-follow-container compact yui3-u-1-4 m-yui3-u-1'] if self.is_onboarding: classes.append('onboarding') tree = html.div(class_=classes, *[ html.a(class_='x-button close icon-remove muted', href='#', data={'not-interested-btn': 1, 'user-id': self.target_id}, alt='Not Interested'), html.div(class_='compact-inner', *[ html.div(class_='yui3-u-1', *[ html.a_or_span(class_='avatar', make_link=not self.is_onboarding, style={'background-image': 'url(%s)' % self.target_avatar}, href=self.target_user_detail_url), ]), html.div(class_='username ta-center', *[ html.a_or_span(href=self.target_user_detail_url, make_link=not self.is_onboarding, *[ self.target_username ]), ]), html.div(class_='post-text note-style user-description-block yui3-u-1 ta-center', *[ html.span(truncated_description), ]), html.div(class_='follow-button-container ta-center', *[ self.follow_button_presenter.generate_html() ]), ]) ]) return tree
def generate_post_body_text(self): body_text = '[Post deleted]' if not self.post_a.get('is_deleted'): body_text = build_tree_from_text_entity_pack( self.request, self.post_a) text_block = html.div( class_='post-text', *[html.span(class_='post-content e-content', *[ body_text, ])]) return text_block
def generate_html(self): if self.action not in self.allowed_actions: return '' timestamp = html.span(class_=('pull-right', 'timestamp'), title=self.interaction_date.strftime("%I:%M %p - %d %b %Y"), *[ html.i(class_='icon-time yui3-u'), " " + naturaldate(self.interaction_date), ]) text = html.div(*[ self.generate_icon(), self.generate_source_users(), ' ', self.generate_verb(), ' ', self.generate_obj_link, '.', timestamp ]) facepile = html.div(class_=('interaction-facepiles',), *self.generate_facepiles()) return html.div(class_='post-container interaction subpixel', *[text, facepile])
def generate_html(self): data = { 'backfill-control': 1, 'before-id': self.min_id, 'since-id': self.marker_id, } return [ html.div(class_='backfill-container', *[ html.div(class_='content ta-center', *[ html.a(href='#', data=data, *[ html.span(class_='yui3-u', *[ html.i(class_='icon-circle-arrow-up') ]), html.span(class_='yui3-u text', *[ ' Load More' ]) ]), html.div(class_='hide spinner-container', *[ html.span(class_='loading-spinner hide', *['']) ]), ]), ])]
def generate_html(self): if self.action not in self.allowed_actions: return '' timestamp = html.span( class_=('pull-right', 'timestamp'), title=self.interaction_date.strftime("%I:%M %p - %d %b %Y"), *[ html.i(class_='icon-time yui3-u'), " " + naturaldate(self.interaction_date), ]) text = html.div(*[ self.generate_icon(), self.generate_source_users(), ' ', self.generate_verb(), ' ', self.generate_obj_link, '.', timestamp ]) facepile = html.div(class_=('interaction-facepiles', ), *self.generate_facepiles()) return html.div(class_='post-container interaction subpixel', *[text, facepile])
def generate_html(self): data = { 'backfill-control': 1, 'before-id': self.min_id, 'since-id': self.marker_id, } return [ html.div( class_='backfill-container', *[ html.div( class_='content ta-center', *[ html.a( href='#', data=data, *[ html.span( class_='yui3-u', *[ html.i( class_='icon-circle-arrow-up') ]), html.span(class_='yui3-u text', *[' Load More']) ]), html.div(class_='hide spinner-container', *[ html.span( class_='loading-spinner hide', *['']) ]), ]), ]) ]
def generate_post_header(self): username_block = html.span(class_='username') if self.post_a.user: username_block = html.span(class_='username p-author h-card', *[ html.a(href=self.user_detail_url, data=self.click_data, class_='p-nickname u-url', *[self.post_a.user.username]) ]) header_items = [] # star this post if self.is_authenticated: if self.show_star_button: star_presenter = StarPresenter.from_data( self.request, self.post_a) star_html = star_presenter.generate_html() header_items.append(html.li(class_='yui3-u', *[star_html])) if self.show_repost_button: repost_button = RepostButtonPresenter.from_data( self.request, self.post_a) repost_button_html = repost_button.generate_html() if repost_button_html != '': header_items.append( html.li(class_='yui3-u repost', *[repost_button_html])) return html.div( class_='post-header', *[ username_block, html. ul(class_='ul-horizontal unstyled yui3-u fixed-right ta-right', *header_items) ]) return username_block
def generate_post_header(self): username_block = html.span(class_='username') if self.post_a.user: username_block = html.span(class_='username p-author h-card', *[ html.a(href=self.user_detail_url, data=self.click_data, class_='p-nickname u-url', *[ self.post_a.user.username ]) ]) header_items = [] # star this post if self.is_authenticated: if self.show_star_button: star_presenter = StarPresenter.from_data(self.request, self.post_a) star_html = star_presenter.generate_html() header_items.append( html.li(class_='yui3-u', *[ star_html ]) ) if self.show_repost_button: repost_button = RepostButtonPresenter.from_data(self.request, self.post_a) repost_button_html = repost_button.generate_html() if repost_button_html != '': header_items.append( html.li(class_='yui3-u repost', *[ repost_button_html ]) ) return html.div(class_='post-header', *[ username_block, html.ul(class_='ul-horizontal unstyled yui3-u fixed-right ta-right', *header_items) ]) return username_block
def file_upload_progress(image_name='', uploaded=False): hide_progress = 'hide' text_class = '' if uploaded: hide_progress = 'plain' text_class = ' text-success' return html.div(class_='file-related yui3-u', *[ html.div(class_='hide', *[( html.input(type_='file', name='upload_file', data={'file-upload-input': 1}) )]), html.div(class_='file-preview', data={'image-preview-container': 1}, *[ html.div(class_=hide_progress, data={'upload-progress': 1}, *[ html.div(class_='progress', *[ html.span(class_='image-name', data={'img-name': 1}, *[ html.i(class_='icon icon-remove-sign transition-color muted' + hide_progress, data={'remove-attachment': 1}), html.span(class_='image-name-text' + text_class, data={'text': 1}, *[image_name]) ]), html.div(class_='bar', style={'width': '0%'}) ]) ]) ]) ])
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 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 generate_append_post_url(self): return html.div(class_='ta-left', *[ html.p(*[ '%s will automatically be appended to your post.' % (self.appended_post_url) ]) ])
def generate_textarea_container(self): return html.div(class_='text-area layout-like-p subpixel"', *[ self.generate_textarea() ])
def generate_html(self): if self.post_a.get('is_deleted') and not self.show_deleted: return '' post_data = { 'post-id': self.post_a.id, # Should the post be remove from the page. # or should we insert [deleted] text 'post-remove-from-page': '0' if self.show_deleted else '1' } if self.post_a.user: post_data['post-author-username'] = self.post_a.user.username post_data['post-author-id'] = self.post_a.user.id else: post_data['post-author-deleted'] = '1' if self.repost: post_data['post-parent-id'] = self.repost.id classes = ['post-container', 'subpixel'] if not self.single_post and self.in_conversation: classes += ['p-in-reply-to', 'h-cite'] else: classes += ['h-entry'] if self.post_a.get('is_deleted'): classes.append('deleted') if self.hidden: classes.append('hide') avatar_size = 114 avatar_classes = ['avatar'] if self.single_post: classes.append('single-post-container') avatar_classes.append('large') avatar_size = 160 avatar_block = '' if self.post_a.user: avatar_url = append_query_string(self.post_a.user.avatar_image.url, params={ 'w': avatar_size, 'h': avatar_size }) avatar_block = html.div( class_='media', *[ html.a(class_=avatar_classes, data=self.click_data, style={'background-image': 'url(%s)' % avatar_url}, href=self.user_detail_url) ]) tree = html.div(class_=classes, data=post_data, name=str(self.post_a.id), *[ html.div(class_='content', *[ avatar_block, self.generate_post_header(), self.generate_post_body(), self.generate_post_footer() ]), ]) return tree
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