def import_contact(cls, addr): """ Fetch information about a Diaspora user and import it into the Contact provided. """ try: wf = WebfingerRequest(addr).fetch() except URLError as e: current_app.logger.warning(e) current_app.logger.warning(e.readlines()) return None if not wf: return None NS = {'XRD': 'http://docs.oasis-open.org/ns/xri/xrd-1.0'} c = Contact() pk = wf.xpath('//XRD:Link[@rel="diaspora-public-key"]/@href', namespaces=NS)[0] c.public_key = b64decode(pk).decode("ascii") hcard_url = wf.xpath( '//XRD:Link[@rel="http://microformats.org/profile/hcard"]/@href', namespaces=NS)[0] req = Request(hcard_url) req.add_header('User-Agent', USER_AGENT) hcard = html.parse(urlopen(req, timeout=10)) c.realname = hcard.xpath('//*[@class="fn"]')[0].text pod_loc = hcard.xpath('//*[@id="pod_location"]')[0].text photo_url = hcard.xpath('//*[@class="entity_photo"]//img/@src')[0] if photo_url: try: mp = import_url_as_mimepart(urljoin(pod_loc, photo_url)) except: current_app.logger.debug(format_exc()) else: mp.text_preview = u'(picture for {0})'.format(c.realname or '(anonymous)') c.avatar = mp username = wf.xpath('//XRD:Subject/text()', namespaces=NS)[0].split(':')[1] guid = wf.xpath(".//XRD:Link[@rel='http://joindiaspora.com/guid']", namespaces=NS)[0].get("href") server = wf.xpath( ".//XRD:Link[@rel='http://joindiaspora.com/seed_location']", namespaces=NS)[0].get("href") d = cls(contact=c, guid=guid, username=username, server=server) db.session.add(d) db.session.add(c) try: d.import_public_posts() except: current_app.logger.debug(format_exc()) return d
def import_contact(cls, addr): """ Fetch information about a Diaspora user and import it into the Contact provided. """ try: wf = WebfingerRequest(addr).fetch() except URLError: return None if not wf: return None NS = {'XRD': 'http://docs.oasis-open.org/ns/xri/xrd-1.0'} c = Contact() pk = wf.xpath('//XRD:Link[@rel="diaspora-public-key"]/@href', namespaces=NS)[0] c.public_key = b64decode(pk).decode("ascii") hcard_url = wf.xpath( '//XRD:Link[@rel="http://microformats.org/profile/hcard"]/@href', namespaces=NS )[0] hcard = html.parse(urlopen(hcard_url)) c.realname = hcard.xpath('//*[@class="fn"]')[0].text pod_loc = hcard.xpath('//*[@id="pod_location"]')[0].text photo_url = hcard.xpath('//*[@class="entity_photo"]//img/@src')[0] if photo_url: mp = import_url_as_mimepart(urljoin(pod_loc, photo_url)) mp.text_preview = '(picture for {0})'.format( c.realname or '(anonymous)' ) c.avatar = mp username = wf.xpath( '//XRD:Subject/text()', namespaces=NS )[0].split(':')[1] guid = wf.xpath( ".//XRD:Link[@rel='http://joindiaspora.com/guid']", namespaces=NS )[0].get("href") server = wf.xpath( ".//XRD:Link[@rel='http://joindiaspora.com/seed_location']", namespaces=NS )[0].get("href") d = cls( contact=c, guid=guid, username=username, server=server ) db.session.add(d) db.session.add(c) return d
def save_contact_groups(contact_id, _user): """ Change which SubscriptionGroups a contact is in by parsing a string of keywords (like tag processing). Any new terms will create new groups; any now-empty groups will be deleted. """ contact = Contact.get(contact_id) if not contact: abort(404, 'No such contact', force_status=True) sub = _user.contact.subscribed_to(contact) if not sub: abort(400, 'Not subscribed') groups = post_param( 'groups', template='roster_edit_group.tpl', optional=True) or '' new_groups = dict( (g.name, g) for g in SubscriptionGroup.parse_line(groups, create=True, user=_user)) old_groups = dict((g.name, g) for g in sub.groups) for group_name, group in old_groups.items(): if group_name not in new_groups: other_members = [ s for s in group.subscriptions if s.to_id != contact.id ] if not other_members: db.session.delete(group) sub.groups = list(new_groups.values()) db.session.add(sub) db.session.commit() return redirect(url_for('.view', _external=True))
def edit_contact_groups_form(contact_id, _user): """ Form to edit which SubscriptionGroups a contact is in. """ contact = Contact.get(contact_id) if not contact: abort(404, 'No such contact') sub = _user.contact.subscribed_to(contact) if not sub: abort(404, 'No such contact') data = { 'actions': { 'save_groups': url_for( '.save_contact_groups', contact_id=contact.id, _external=True ) }, 'subscription': json_contact_with_groups(sub, _user) } add_logged_in_user_to_data(data, _user) return render_response('roster_edit_group.tpl', data)
def __init__(self, contact=None): """ Creates a new user, creating a new Contact for the user if none is supplied. The contact is then associated with the newly created User. """ db.Model.__init__(self) if not contact: contact = Contact() self.contact = contact db.session.add(self)
def webfinger(contact_addr): """ Returns the Webfinger profile for a contact called <contact> (in "user@host" form). """ contact_id, _ = contact_addr.split('@') c = Contact.get(int(contact_id)) if not c or not c.user or not c.user.activated: abort(404, 'No such contact') diasp = DiasporaContact.get_for_contact(c) ns = 'http://docs.oasis-open.org/ns/xri/xrd-1.0' doc = etree.Element("{%s}XRD" % ns, nsmap={None: ns}) etree.SubElement(doc, "Subject").text = "acct:%s" % diasp.username etree.SubElement(doc, "Alias").text = \ '"{0}"'.format(url_for('index', _external=True)) etree.SubElement( doc, "Link", rel='http://microformats.org/profile/hcard', type='text/html', href=url_for('.hcard', guid=diasp.guid, _external=True) ) etree.SubElement( doc, "Link", rel='http://joindiaspora.com/seed_location', type='text/html', href=url_for('index', _external=True) ) etree.SubElement( doc, "Link", rel='http://joindiaspora.com/guid', type='text/html', href=diasp.guid ) etree.SubElement( doc, "Link", rel='http://webfinger.net/rel/profile-page', type='text/html', href=url_for('contacts.profile', contact_id=c.id, _external=True) ) etree.SubElement( doc, "Link", rel='http://schemas.google.com/g/2010#updates-from', type='application/atom+xml', href=url_for('contacts.feed', contact_id=c.id, _external=True) ) etree.SubElement( doc, "Link", rel='diaspora-public-key', type='RSA', href=b64encode(c.public_key.encode('ascii')) ) return send_xml(doc)
def subscribe(contact_id, _user): """ Add a contact to the logged-in users roster. """ contact = Contact.get(contact_id) if not contact: abort(404, 'No such contact', force_status=True) _user.contact.subscribe(contact) db.session.commit() return redirect(url_for('contacts.profile', contact_id=contact.id))
def pyaspora_subscribe(part, fmt, url): """ Standard message for when a contact subscribes to you. """ from pyaspora.contact.models import Contact if fmt != 'text/html' or not part.inline: return None payload = loads(part.mime_part.body.decode('utf-8')) to_contact = Contact.get(payload['to']) return render_template_string( 'subscribed to <a href="{{profile}}">{{name}}</a>', profile=url_for('contacts.profile', contact_id=to_contact.id, _external=True), name=to_contact.realname)
def _profile_base(contact_id, public=False): """ Standard data for profile-alike pages, including the profile page and feed pages. """ from pyaspora.post.models import Post, Share from pyaspora.post.views import json_posts contact = Contact.get(contact_id) if not contact: abort(404, 'No such contact', force_status=True) viewing_as = None if public else logged_in_user() data = json_contact(contact, viewing_as) limit = int(request.args.get('limit', 25)) if viewing_as and request.args.get('refresh', False) and contact.diasp: try: contact.diasp.import_public_posts() db.session.commit() except: current_app.logger.debug(format_exc()) # If not local, we don't have a proper feed if viewing_as or contact.user: # user put it on their public wall feed_query = Post.Queries.public_wall_for_contact(contact) if viewing_as: # Also include things this user has shared with us shared_query = Post.Queries.author_shared_with( contact, viewing_as) feed_query = or_(feed_query, shared_query) feed = db.session.query(Share). \ join(Post). \ filter(feed_query). \ order_by(desc(Post.thread_modified_at)). \ group_by(Post.id). \ options(contains_eager(Share.post)). \ limit(limit) data['feed'] = json_posts([(s.post, s) for s in feed], viewing_as) add_logged_in_user_to_data(data, viewing_as) return data, contact
def _profile_base(contact_id, public=False): """ Standard data for profile-alike pages, including the profile page and feed pages. """ from pyaspora.post.models import Post, Share from pyaspora.post.views import json_posts contact = Contact.get(contact_id) if not contact: abort(404, 'No such contact', force_status=True) viewing_as = None if public else logged_in_user() data = json_contact(contact, viewing_as) limit = int(request.args.get('limit', 25)) if viewing_as and request.args.get('refresh', False) and contact.diasp: try: contact.diasp.import_public_posts() db.session.commit() except: current_app.logger.debug(format_exc()) # If not local, we don't have a proper feed if viewing_as or contact.user: # user put it on their public wall feed_query = Post.Queries.public_wall_for_contact(contact) if viewing_as: # Also include things this user has shared with us shared_query = Post.Queries.author_shared_with(contact, viewing_as) feed_query = or_(feed_query, shared_query) feed = db.session.query(Share). \ join(Post). \ filter(feed_query). \ order_by(desc(Post.thread_modified_at)). \ group_by(Post.id). \ options(contains_eager(Share.post)). \ limit(limit) data['feed'] = json_posts([(s.post, s) for s in feed], viewing_as) add_logged_in_user_to_data(data, viewing_as) return data, contact
def avatar(contact_id): """ Display the photo (or other media item) that represents a Contact. If the user is logged in they can view the avatar for any contact, but if not logged in then only locally-mastered contacts have their avatar displayed. """ contact = Contact.get(contact_id) if not contact: abort(404, 'No such contact', force_status=True) if not contact.user and not logged_in_user(): abort(404, 'No such contact', force_status=True) part = contact.avatar if not part: abort(404, 'Contact has no avatar', force_status=True) return raw_response(part.body, part.type)
def pyaspora_subscribe(part, fmt, url): """ Standard message for when a contact subscribes to you. """ from pyaspora.contact.models import Contact if fmt != 'text/html' or not part.inline: return None payload = loads(part.mime_part.body.decode('utf-8')) to_contact = Contact.get(payload['to']) return render_template_string( 'subscribed to <a href="{{profile}}">{{name}}</a>', profile=url_for( 'contacts.profile', contact_id=to_contact.id, _external=True ), name=to_contact.realname )
def subscriptions(contact_id, _user): """ Display the friend list for the contact (who must be local to this server, because this server doesn't hold the full friend list for remote users). """ contact = Contact.get(contact_id) if not (contact.user and contact.user.activated): abort(404, 'No such contact', force_status=True) # Looking at our own list? You'll be wanting the edit view. if contact.id == _user.contact.id: return redirect(url_for('roster.view', _external=True)) data = json_contact(contact, _user) data['subscriptions'] = [json_contact(c, _user) for c in contact.friends()] add_logged_in_user_to_data(data, _user) return render_response('contacts_friend_list.tpl', data)
def _fill_cache(c, show_shares=False): # Fill the cache in bulk, which will also fill the entries post_ids = c['post'].keys() if post_ids: for post_tag in PostTag.get_tags_for_posts(post_ids): c['post'][post_tag.post_id]['tags'].append(json_tag(post_tag.tag)) post_parts = PostPart.get_parts_for_posts(post_ids). \ order_by(PostPart.order) for post_part in post_parts: c['post'][post_part.post_id]['parts'].append(json_part(post_part)) if show_shares: for post_share in Share.get_for_posts(post_ids): post_id = post_share.post_id if not c['post'][post_id]['shares']: c['post'][post_id]['shares'] = [] c['post'][post_id]['shares'].append( json_share(post_share, cache=c)) if c['contact']: for contact in Contact.get_many(c['contact'].keys()): c['contact'][contact.id].update(json_contact(contact))
def subscriptions(contact_id, _user): """ Display the friend list for the contact (who must be local to this server, because this server doesn't hold the full friend list for remote users). """ contact = Contact.get(contact_id) if not(contact.user and contact.user.activated): abort(404, 'No such contact', force_status=True) # Looking at our own list? You'll be wanting the edit view. if contact.id == _user.contact.id: return redirect(url_for('roster.view', _external=True)) data = json_contact(contact, _user) data['subscriptions'] = [json_contact(c, _user) for c in contact.friends()] add_logged_in_user_to_data(data, _user) return render_response('contacts_friend_list.tpl', data)
def _fill_cache(c, show_shares=False): # Fill the cache in bulk, which will also fill the entries post_ids = c['post'].keys() if post_ids: for post_tag in PostTag.get_tags_for_posts(post_ids): c['post'][post_tag.post_id]['tags'].append(json_tag(post_tag.tag)) post_parts = PostPart.get_parts_for_posts(post_ids). \ order_by(PostPart.order) for post_part in post_parts: c['post'][post_part.post_id]['parts'].append(json_part(post_part)) if show_shares: for post_share in Share.get_for_posts(post_ids): post_id = post_share.post_id if not c['post'][post_id]['shares']: c['post'][post_id]['shares'] = [] c['post'][post_id]['shares'].append( json_share(post_share, cache=c) ) if c['contact']: for contact in Contact.get_many(c['contact'].keys()): c['contact'][contact.id].update(json_contact(contact))
def save_contact_groups(contact_id, _user): """ Change which SubscriptionGroups a contact is in by parsing a string of keywords (like tag processing). Any new terms will create new groups; any now-empty groups will be deleted. """ contact = Contact.get(contact_id) if not contact: abort(404, 'No such contact', force_status=True) sub = _user.contact.subscribed_to(contact) if not sub: abort(400, 'Not subscribed') groups = post_param( 'groups', template='roster_edit_group.tpl', optional=True ) or '' new_groups = dict( (g.name, g) for g in SubscriptionGroup.parse_line(groups, create=True, user=_user) ) old_groups = dict((g.name, g) for g in sub.groups) for group_name, group in old_groups.items(): if group_name not in new_groups: other_members = [ s for s in group.subscriptions if s.to_id != contact.id ] if not other_members: db.session.delete(group) sub.groups = list(new_groups.values()) db.session.add(sub) db.session.commit() return redirect(url_for('.view', _external=True))
def edit_contact_groups_form(contact_id, _user): """ Form to edit which SubscriptionGroups a contact is in. """ contact = Contact.get(contact_id) if not contact: abort(404, 'No such contact') sub = _user.contact.subscribed_to(contact) if not sub: abort(404, 'No such contact') data = { 'actions': { 'save_groups': url_for('.save_contact_groups', contact_id=contact.id, _external=True) }, 'subscription': json_contact_with_groups(sub, _user) } add_logged_in_user_to_data(data, _user) return render_response('roster_edit_group.tpl', data)