def write_response(self, response, actor=None, url=None, title=None): """Converts ActivityStreams activities and writes them out. Args: response: response dict with values based on OpenSocial ActivityStreams REST API, as returned by Source.get_activities_response() actor: optional ActivityStreams actor dict for current user. Only used for Atom output. url: the input URL title: string, Used in Atom output """ expected_formats = ('activitystreams', 'json', 'atom', 'xml', 'html', 'json-mf2') format = self.request.get('format') or self.request.get('output') or 'json' if format not in expected_formats: raise exc.HTTPBadRequest('Invalid format: %s, expected one of %r' % (format, expected_formats)) activities = response['items'] self.response.headers.update({ 'Access-Control-Allow-Origin': '*', 'Strict-Transport-Security': 'max-age=16070400; includeSubDomains; preload', # 6 months }) if format in ('json', 'activitystreams'): # list of official MIME types: # https://www.iana.org/assignments/media-types/media-types.xhtml self.response.headers['Content-Type'] = 'application/json' self.response.out.write(json.dumps(response, indent=2)) elif format == 'atom': self.response.headers['Content-Type'] = 'application/atom+xml' hub = self.request.get('hub') self.response.out.write(atom.activities_to_atom( activities, actor, host_url=url or self.request.host_url + '/', request_url=self.request.url, xml_base=util.base_url(url), title=title, rels={'hub': hub} if hub else None)) self.response.headers.add('Link', str('<%s>; rel="self"' % self.request.url)) if hub: self.response.headers.add('Link', str('<%s>; rel="hub"' % hub)) elif format == 'xml': self.response.headers['Content-Type'] = 'application/xml' self.response.out.write(XML_TEMPLATE % util.to_xml(response)) elif format == 'html': self.response.headers['Content-Type'] = 'text/html' self.response.out.write(microformats2.activities_to_html(activities)) elif format == 'json-mf2': self.response.headers['Content-Type'] = 'application/json' items = [microformats2.object_to_json(a) for a in activities] self.response.out.write(json.dumps({'items': items}, indent=2)) if 'plaintext' in self.request.params: # override response content type self.response.headers['Content-Type'] = 'text/plain'
def write_response(self, response, actor=None, url=None, title=None): """Converts ActivityStreams activities and writes them out. Args: response: response dict with values based on OpenSocial ActivityStreams REST API, as returned by Source.get_activities_response() actor: optional ActivityStreams actor dict for current user. Only used for Atom output. url: the input URL title: string, Used in Atom output """ expected_formats = ('activitystreams', 'json', 'atom', 'xml', 'html', 'json-mf2') format = self.request.get('format') or self.request.get('output') or 'json' if format not in expected_formats: raise exc.HTTPBadRequest('Invalid format: %s, expected one of %r' % (format, expected_formats)) activities = response['items'] self.response.headers.update({ 'Access-Control-Allow-Origin': '*', 'Strict-Transport-Security': 'max-age=16070400; includeSubDomains; preload', # 6 months }) if format in ('json', 'activitystreams'): self.response.headers['Content-Type'] = 'application/json' self.response.out.write(json.dumps(response, indent=2)) elif format == 'atom': self.response.headers['Content-Type'] = 'text/xml' hub = self.request.get('hub') self.response.out.write(atom.activities_to_atom( activities, actor, host_url=url or self.request.host_url + '/', request_url=self.request.url, xml_base=util.base_url(url), title=title, rels={'hub': hub} if hub else None)) self.response.headers.add('Link', str('<%s>; rel="self"' % self.request.url)) if hub: self.response.headers.add('Link', str('<%s>; rel="hub"' % hub)) elif format == 'xml': self.response.headers['Content-Type'] = 'text/xml' self.response.out.write(XML_TEMPLATE % util.to_xml(response)) elif format == 'html': self.response.headers['Content-Type'] = 'text/html' self.response.out.write(microformats2.activities_to_html(activities)) elif format == 'json-mf2': self.response.headers['Content-Type'] = 'application/json' items = [microformats2.object_to_json(a) for a in activities] self.response.out.write(json.dumps({'items': items}, indent=2)) if 'plaintext' in self.request.params: # override response content type self.response.headers['Content-Type'] = 'text/plain'
def write_response(self, response, actor=None): """Converts ActivityStreams activities and writes them out. Args: response: response dict with values based on OpenSocial ActivityStreams REST API, as returned by Source.get_activities_response() actor: optional ActivityStreams actor dict for current user. Only used for Atom output. """ expected_formats = ('activitystreams', 'json', 'atom', 'xml', 'html', 'json-mf2') format = self.request.get('format') or self.request.get('output') or 'json' if format not in expected_formats: raise exc.HTTPBadRequest('Invalid format: %s, expected one of %r' % (format, expected_formats)) activities = response['items'] self.response.headers['Access-Control-Allow-Origin'] = '*' if format in ('json', 'activitystreams'): self.response.headers['Content-Type'] = 'application/json' self.response.out.write(json.dumps(response, indent=2)) elif format == 'atom': self.response.headers['Content-Type'] = 'text/xml' self.response.out.write(atom.activities_to_atom( activities, actor, host_url=self.request.host_url + '/', request_url=self.request.path_url)) elif format == 'xml': self.response.headers['Content-Type'] = 'text/xml' self.response.out.write(XML_TEMPLATE % util.to_xml(response)) elif format == 'html': self.response.headers['Content-Type'] = 'text/html' self.response.out.write(microformats2.activities_to_html(activities)) elif format == 'json-mf2': self.response.headers['Content-Type'] = 'application/json' items = [microformats2.object_to_json(a['object'], a.get('context', {})) for a in activities] self.response.out.write(json.dumps({'items': items}, indent=2)) if 'plaintext' in self.request.params: # override response content type self.response.headers['Content-Type'] = 'text/plain'
def write_response(self, response, actor=None, url=None, title=None, hfeed=None): """Converts ActivityStreams activities and writes them out. Args: response: response dict with values based on OpenSocial ActivityStreams REST API, as returned by Source.get_activities_response() actor: optional ActivityStreams actor dict for current user. Only used for Atom and JSON Feed output. url: the input URL title: string, used in feed output (Atom, JSON Feed, RSS) hfeed: dict, parsed mf2 h-feed, if available """ format = self.request.get('format') or self.request.get( 'output') or 'json' if format not in FORMATS: raise exc.HTTPBadRequest('Invalid format: %s, expected one of %r' % (format, FORMATS)) if 'plaintext' in self.request.params: # override content type self.response.headers['Content-Type'] = 'text/plain' else: content_type = FORMATS.get(format) if content_type: self.response.headers['Content-Type'] = content_type if self.request.method == 'HEAD': return activities = response['items'] try: if format in ('as1', 'json', 'activitystreams'): self.response.out.write(json_dumps(response, indent=2)) elif format == 'as2': response.update({ 'items': [as2.from_as1(a) for a in activities], 'totalItems': response.pop('totalResults', None), 'updated': response.pop('updatedSince', None), 'filtered': None, 'sorted': None, }) self.response.out.write( json_dumps(util.trim_nulls(response), indent=2)) elif format == 'atom': hub = self.request.get('hub') reader = self.request.get('reader', 'true').lower() if reader not in ('true', 'false'): self.abort(400, 'reader param must be either true or false') if not actor and hfeed: actor = microformats2.json_to_object({ 'properties': hfeed.get('properties', {}), }) self.response.out.write( atom.activities_to_atom(activities, actor, host_url=url or self.request.host_url + '/', request_url=self.request.url, xml_base=util.base_url(url), title=title, rels={'hub': hub} if hub else None, reader=(reader == 'true'))) self.response.headers.add( 'Link', str('<%s>; rel="self"' % self.request.url)) if hub: self.response.headers.add('Link', str('<%s>; rel="hub"' % hub)) elif format == 'rss': if not title: title = 'Feed for %s' % url self.response.out.write( rss.from_activities(activities, actor, title=title, feed_url=self.request.url, hfeed=hfeed, home_page_url=util.base_url(url))) elif format in ('as1-xml', 'xml'): self.response.out.write(XML_TEMPLATE % util.to_xml(response)) elif format == 'html': self.response.out.write( microformats2.activities_to_html(activities)) elif format in ('mf2-json', 'json-mf2'): items = [microformats2.activity_to_json(a) for a in activities] self.response.out.write(json_dumps({'items': items}, indent=2)) elif format == 'jsonfeed': try: jf = jsonfeed.activities_to_jsonfeed( activities, actor=actor, title=title, feed_url=self.request.url) except TypeError as e: raise exc.HTTPBadRequest('Unsupported input data: %s' % e) self.response.out.write(json_dumps(jf, indent=2)) except ValueError as e: logging.warning('converting to output format failed', stack_info=True) self.abort(400, 'Could not convert to %s: %s' % (format, str(e)))
def write_response(self, response, actor=None, url=None, title=None): """Converts ActivityStreams activities and writes them out. Args: response: response dict with values based on OpenSocial ActivityStreams REST API, as returned by Source.get_activities_response() actor: optional ActivityStreams actor dict for current user. Only used for Atom and JSON Feed output. url: the input URL title: string, Used in Atom and JSON Feed output """ expected_formats = ('activitystreams', 'json', 'atom', 'xml', 'html', 'json-mf2', 'jsonfeed') format = self.request.get('format') or self.request.get( 'output') or 'json' if format not in expected_formats: raise exc.HTTPBadRequest('Invalid format: %s, expected one of %r' % (format, expected_formats)) activities = response['items'] if format in ('json', 'activitystreams'): # list of official MIME types: # https://www.iana.org/assignments/media-types/media-types.xhtml self.response.headers['Content-Type'] = 'application/json' self.response.out.write(json.dumps(response, indent=2)) elif format == 'atom': self.response.headers['Content-Type'] = 'application/atom+xml' hub = self.request.get('hub') reader = self.request.get('reader', 'true').lower() if reader not in ('true', 'false'): self.abort(400, 'reader param must be either true or false') self.response.out.write( atom.activities_to_atom(activities, actor, host_url=url or self.request.host_url + '/', request_url=self.request.url, xml_base=util.base_url(url), title=title, rels={'hub': hub} if hub else None, reader=(reader == 'true'))) self.response.headers.add( 'Link', str('<%s>; rel="self"' % self.request.url)) if hub: self.response.headers.add('Link', str('<%s>; rel="hub"' % hub)) elif format == 'xml': self.response.headers['Content-Type'] = 'application/xml' self.response.out.write(XML_TEMPLATE % util.to_xml(response)) elif format == 'html': self.response.headers['Content-Type'] = 'text/html' self.response.out.write( microformats2.activities_to_html(activities)) elif format == 'json-mf2': self.response.headers['Content-Type'] = 'application/json' items = [microformats2.activity_to_json(a) for a in activities] self.response.out.write(json.dumps({'items': items}, indent=2)) elif format == 'jsonfeed': self.response.headers['Content-Type'] = 'application/json' try: jf = jsonfeed.activities_to_jsonfeed(activities, actor=actor, title=title, feed_url=self.request.url) except TypeError as e: raise exc.HTTPBadRequest('Unsupported input data: %s' % e) self.response.out.write(json.dumps(jf, indent=2)) if 'plaintext' in self.request.params: # override response content type self.response.headers['Content-Type'] = 'text/plain'
def get(self): """Handles an API GET. Request path is of the form /user_id/group_id/app_id/activity_id , where each element is an optional string object id. """ args = {key: self.request.params[key] for key in ('access_token', 'access_token_key', 'access_token_secret') if key in self.request.params} source = self.source_class()(**args) # parse path args = urllib.unquote(self.request.path).strip('/').split('/') if len(args) > MAX_PATH_LEN: raise exc.HTTPNotFound() elif args == ['']: args = [] # handle default path elements args = [None if a in defaults else a for a, defaults in zip(args, PATH_DEFAULTS)] user_id = args[0] if args else None paging_params = self.get_paging_params() # extract format expected_formats = ('json', 'atom', 'xml', 'html', 'json-mf2') format = self.request.get('format', 'json') if format not in expected_formats: raise exc.HTTPBadRequest('Invalid format: %s, expected one of %r' % (format, expected_formats)) # get activities and build response response = source.get_activities_response(*args, **paging_params) activities = response['items'] if format == 'atom': # strip the access token from the request URL before returning params = dict(self.request.GET.items()) for key in 'access_token', 'access_token_key', 'access_token_secret': if key in params: del params[key] request_url = '%s?%s' % (self.request.path_url, urllib.urlencode(params)) actor = source.get_actor(user_id) response.update({ 'host_url': self.request.host_url + "/", 'request_url': request_url, 'title': 'User feed for ' + actor.get('displayName', 'unknown'), 'updated': activities[0]['object'].get('published') if activities else '', 'actor': actor, }) # TODO: render activity contents as html. i'd need to allow markup # through, though, which iirc has broken before when posts have included # HTML entities. # for activity in response.get('items', []): # obj = activity['object'] # obj['content'] = microformats2.render_content(obj) # encode and write response self.response.headers['Access-Control-Allow-Origin'] = '*' if format == 'json': self.response.headers['Content-Type'] = 'application/json' self.response.out.write(json.dumps(response, indent=2)) elif format == 'atom': self.response.headers['Content-Type'] = 'text/xml' self.response.out.write(template.render(ATOM_TEMPLATE_FILE, response)) elif format == 'xml': self.response.headers['Content-Type'] = 'text/xml' self.response.out.write(XML_TEMPLATE % util.to_xml(response)) elif format == 'html': self.response.headers['Content-Type'] = 'text/html' items = [microformats2.object_to_html(a['object']) for a in activities] self.response.out.write("""\ <!DOCTYPE html> <html> <head><meta charset="utf-8"></head> <body> %s </body> </html> """ % '\n'.join(items)) elif format == 'json-mf2': self.response.headers['Content-Type'] = 'application/json' items = [microformats2.object_to_json(a['object']) for a in activities] self.response.out.write(json.dumps({'items': items}, indent=2)) if 'plaintext' in self.request.params: # override response content type self.response.headers['Content-Type'] = 'text/plain'
def write_response(self, response, actor=None, url=None, title=None, hfeed=None): """Converts ActivityStreams activities and writes them out. Args: response: response dict with values based on OpenSocial ActivityStreams REST API, as returned by Source.get_activities_response() actor: optional ActivityStreams actor dict for current user. Only used for Atom and JSON Feed output. url: the input URL title: string, used in feed output (Atom, JSON Feed, RSS) hfeed: dict, parsed mf2 h-feed, if available """ format = self.request.get('format') or self.request.get('output') or 'json' if format not in FORMATS: raise exc.HTTPBadRequest('Invalid format: %s, expected one of %r' % (format, FORMATS)) activities = response['items'] try: if format in ('as1', 'json', 'activitystreams'): # list of official MIME types: # https://www.iana.org/assignments/media-types/media-types.xhtml self.response.headers['Content-Type'] = \ 'application/json' if format == 'json' else 'application/stream+json' self.response.out.write(json.dumps(response, indent=2)) elif format == 'as2': self.response.headers['Content-Type'] = 'application/activity+json' response.update({ 'items': [as2.from_as1(a) for a in activities], 'totalItems': response.pop('totalResults', None), 'updated': response.pop('updatedSince', None), 'filtered': None, 'sorted': None, }) self.response.out.write(json.dumps(util.trim_nulls(response), indent=2)) elif format == 'atom': self.response.headers['Content-Type'] = 'application/atom+xml' hub = self.request.get('hub') reader = self.request.get('reader', 'true').lower() if reader not in ('true', 'false'): self.abort(400, 'reader param must be either true or false') self.response.out.write(atom.activities_to_atom( activities, actor, host_url=url or self.request.host_url + '/', request_url=self.request.url, xml_base=util.base_url(url), title=title, rels={'hub': hub} if hub else None, reader=(reader == 'true'))) self.response.headers.add('Link', str('<%s>; rel="self"' % self.request.url)) if hub: self.response.headers.add('Link', str('<%s>; rel="hub"' % hub)) elif format == 'rss': self.response.headers['Content-Type'] = 'application/rss+xml' if not title: title = 'Feed for %s' % url self.response.out.write(rss.from_activities( activities, actor, title=title, feed_url=self.request.url, hfeed=hfeed, home_page_url=util.base_url(url))) elif format in ('as1-xml', 'xml'): self.response.headers['Content-Type'] = 'application/xml' self.response.out.write(XML_TEMPLATE % util.to_xml(response)) elif format == 'html': self.response.headers['Content-Type'] = 'text/html' self.response.out.write(microformats2.activities_to_html(activities)) elif format in ('mf2-json', 'json-mf2'): self.response.headers['Content-Type'] = 'application/json' items = [microformats2.activity_to_json(a) for a in activities] self.response.out.write(json.dumps({'items': items}, indent=2)) elif format == 'jsonfeed': self.response.headers['Content-Type'] = 'application/json' try: jf = jsonfeed.activities_to_jsonfeed(activities, actor=actor, title=title, feed_url=self.request.url) except TypeError as e: raise exc.HTTPBadRequest('Unsupported input data: %s' % e) self.response.out.write(json.dumps(jf, indent=2)) except ValueError as e: logging.warning('converting to output format failed', exc_info=True) self.abort(400, 'Could not convert to %s: %s' % (format, str(e))) if 'plaintext' in self.request.params: # override response content type self.response.headers['Content-Type'] = 'text/plain'
def get(self): """Handles an API GET. Request path is of the form /user_id/group_id/app_id/activity_id , where each element is an optional string object id. """ args = { key: self.request.params[key] for key in ('access_token', 'access_token_key', 'access_token_secret') if key in self.request.params } source = self.source_class()(**args) # parse path args = urllib.unquote(self.request.path).strip('/').split('/') if len(args) > MAX_PATH_LEN: raise exc.HTTPNotFound() elif args == ['']: args = [] # handle default path elements args = [ None if a in defaults else a for a, defaults in zip(args, PATH_DEFAULTS) ] user_id = args[0] if args else None # extract format expected_formats = ('json', 'atom', 'xml', 'html', 'json-mf2') format = self.request.get('format', 'json') if format not in expected_formats: raise exc.HTTPBadRequest('Invalid format: %s, expected one of %r' % (format, expected_formats)) # get activities and build response response = source.get_activities_response(*args, **self.get_kwargs()) activities = response['items'] # encode and write response self.response.headers['Access-Control-Allow-Origin'] = '*' if format == 'json': self.response.headers['Content-Type'] = 'application/json' self.response.out.write(json.dumps(response, indent=2)) elif format == 'atom': actor = source.get_actor(user_id) self.response.headers['Content-Type'] = 'text/xml' self.response.out.write( atom.activities_to_atom(activities, actor, host_url=self.request.host_url + '/', request_url=self.request.path_url)) elif format == 'xml': self.response.headers['Content-Type'] = 'text/xml' self.response.out.write(XML_TEMPLATE % util.to_xml(response)) elif format == 'html': self.response.headers['Content-Type'] = 'text/html' items = [ microformats2.object_to_html(a['object'], a.get('context', {})) for a in activities ] self.response.out.write("""\ <!DOCTYPE html> <html> <head><meta charset="utf-8"></head> <body> %s </body> </html> """ % '\n'.join(items)) elif format == 'json-mf2': self.response.headers['Content-Type'] = 'application/json' items = [ microformats2.object_to_json(a['object'], a.get('context', {})) for a in activities ] self.response.out.write(json.dumps({'items': items}, indent=2)) if 'plaintext' in self.request.params: # override response content type self.response.headers['Content-Type'] = 'text/plain'