Example #1
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).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]
Example #2
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]
Example #3
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
Example #4
0
    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'])