def test_user_page_publish_url_with_unicode_char(self): """Check the custom mf2 we render on social user pages.""" self.sources[0].features = ['publish'] self.sources[0].put() url = 'https://ptt.com/ransomw…ocks-user-access/' Publish(parent=PublishedPage(id=url).key, source=self.sources[0].key).put() user_url = self.sources[0].bridgy_path() resp = self.client.get(user_url) self.assertEqual(200, resp.status_code) parsed = util.parse_mf2(resp.get_data(as_text=True), user_url) publish = parsed['items'][0]['children'][0]
def test_user_page_publish_url_with_unicode_char(self): """Check the custom mf2 we render on social user pages.""" self.sources[0].features = ['publish'] self.sources[0].put() url = 'https://ptt.com/ransomw…ocks-user-access/' Publish(parent=PublishedPage(id=url.encode('utf-8')).key, source=self.sources[0].key).put() user_url = self.sources[0].bridgy_path() resp = app.application.get_response(user_url) self.assertEquals(200, resp.status_int) parsed = util.parse_mf2(resp.body, user_url) publish = parsed['items'][0]['children'][0]
def fetch_mf2(self, url, id=None, require_mf2=True, raise_errors=False): """Fetches a URL and extracts its mf2 data. Side effects: sets :attr:`entity`\ .html on success, calls :attr:`error()` on errors. Args: url: string id: string, optional id of specific element to extract and parse. defaults to the whole page. require_mf2: boolean, whether to return error if no mf2 are found raise_errors: boolean, whether to let error exceptions propagate up or handle them Returns: (:class:`requests.Response`, mf2 data dict) on success, None on failure """ try: resp = util.requests_get(url) resp.raise_for_status() except werkzeug.exceptions.HTTPException: # raised by us, probably via self.error() raise except BaseException as e: if raise_errors: raise util.interpret_http_exception(e) # log exception self.error(f'Could not fetch source URL {url}') if self.entity: self.entity.html = resp.text # parse microformats soup = util.parse_html(resp) mf2 = util.parse_mf2(soup, url=resp.url, id=id) if id and not mf2: self.error(f'Got fragment {id} but no element found with that id.') # special case tumblr's markup: div#content > div.post > div.copy # convert to mf2 and re-parse if not mf2.get('items'): contents = soup.find_all(id='content') if contents: post = contents[0].find_next(class_='post') if post: post['class'] = 'h-entry' copy = post.find_next(class_='copy') if copy: copy['class'] = 'e-content' photo = post.find_next(class_='photo-wrapper') if photo: img = photo.find_next('img') if img: img['class'] = 'u-photo' # TODO: i should be able to pass post or contents[0] to mf2py instead # here, but it returns no items. mf2py bug? doc = str(post) mf2 = util.parse_mf2(doc, resp.url) logger.debug(f'Parsed microformats2: {json_dumps(mf2, indent=2)}') items = mf2.get('items', []) if require_mf2 and (not items or not items[0]): self.error('No microformats2 data found in ' + resp.url, data=mf2, html=f""" No <a href="http://microformats.org/get-started">microformats</a> or <a href="http://microformats.org/wiki/microformats2">microformats2</a> found in <a href="{resp.url}">{util.pretty_link(resp.url)}</a>! See <a href="http://indiewebify.me/">indiewebify.me</a> for details (skip to level 2, <em>Publishing on the IndieWeb</em>). """) return resp, mf2
def test_social_user_page_mf2(self): """Check the custom mf2 we render on social user pages.""" self.sources[0].features = ['listen', 'publish'] self.sources[0].put() # test invite with missing object and content resp = json_loads(self.responses[8].response_json) resp['verb'] = 'invite' resp.pop('object', None) resp.pop('content', None) self.responses[8].response_json = json_dumps(resp) # test that invites render the invitee, not the inviter # https://github.com/snarfed/bridgy/issues/754 self.responses[9].type = 'rsvp' self.responses[9].response_json = json_dumps({ 'id': 'tag:fa.ke,2013:111', 'objectType': 'activity', 'verb': 'invite', 'url': 'http://fa.ke/event', 'actor': { 'displayName': 'Mrs. Host', 'url': 'http://fa.ke/host', }, 'object': { 'objectType': 'person', 'displayName': 'Ms. Guest', 'url': 'http://fa.ke/guest', }, }) for entity in self.responses + self.publishes + self.blogposts: entity.put() user_url = self.sources[0].bridgy_path() response = self.client.get(user_url) self.assertEqual(200, response.status_code) parsed = util.parse_mf2(response.get_data(as_text=True), user_url) hcard = parsed.get('items', [])[0] self.assertEqual(['h-card'], hcard['type']) self.assertEqual(['Fake User'], hcard['properties'].get('name')) self.assertEqual(['http://fa.ke/profile/url'], hcard['properties'].get('url')) self.assertEqual(['enabled'], hcard['properties'].get('bridgy-account-status')) self.assertEqual(['enabled'], hcard['properties'].get('bridgy-listen-status')) self.assertEqual(['enabled'], hcard['properties'].get('bridgy-publish-status')) expected_resps = self.responses[:10] for item, resp in zip(hcard['children'], expected_resps): self.assertIn('h-bridgy-response', item['type']) props = item['properties'] self.assertEqual([resp.status], props['bridgy-status']) self.assertEqual([json_loads(resp.activities_json[0])['url']], props['bridgy-original-source']) self.assertEqual(resp.unsent, props['bridgy-target']) # check invite self.assertIn('Ms. Guest is invited.', response.get_data(as_text=True)) self.assertNotIn('Mrs. Host is invited.', response.get_data(as_text=True)) publish = hcard['children'][len(expected_resps)] self.assertIn('h-bridgy-publish', publish['type']) props = publish['properties'] self.assertEqual([self.publishes[0].key.parent().id()], props['url']) self.assertEqual([self.publishes[0].status], props['bridgy-status'])