Example #1
0
  def create(self, obj, include_link=False, ignore_formatting=False):
    verb = obj.get('verb')
    type = obj.get('objectType')
    if verb == 'like':
      return gr_source.creation_result(
        abort=True, error_plain='Cannot publish likes',
        error_html='Cannot publish likes')
    if 'content' not in obj:
      return gr_source.creation_result(
        abort=False, error_plain='No content',
        error_html='No content')

    if type == 'comment':
      base_url = self.base_object(obj).get('url')
      if not base_url:
        return gr_source.creation_result(
          abort=True,
          error_plain='no %s url to reply to' % self.DOMAIN,
          error_html='no %s url to reply to' % self.DOMAIN)

    content = self._content_for_create(obj, ignore_formatting=ignore_formatting)
    if include_link:
        content += ' - %s' % obj['url']
    ret = {'id': 'fake id', 'url': 'http://fake/url', 'content': content}
    if verb == 'rsvp-yes':
      ret['type'] = 'post'
    return gr_source.creation_result(ret)
Example #2
0
 def preview_create(self, obj, include_link=False):
   if obj.get('verb') == 'like':
     return gr_source.creation_result(
       abort=True, error_plain='Cannot publish likes',
       error_html='Cannot publish likes')
   content = 'preview of ' + obj['content'] + (' - %s' % obj['url']
                                               if include_link else '')
   return gr_source.creation_result(description=content)
Example #3
0
  def preview_create(self, obj, include_link=False, ignore_formatting=False):
    if obj.get('verb') == 'like':
      return gr_source.creation_result(
        abort=True, error_plain='Cannot publish likes',
        error_html='Cannot publish likes')

    content = self._content_for_create(obj, ignore_formatting=ignore_formatting)
    if include_link:
        content += ' - %s' % obj['url']

    content = 'preview of ' + content
    return gr_source.creation_result(description=content)
Example #4
0
  def _render_preview(self, result, include_link=False):
    """Renders a preview CreationResult as HTML.

    Args:
      result: CreationResult
      include_link: boolean

    Returns: CreationResult with the rendered HTML in content
    """
    state = {
      'source_key': self.source.key.urlsafe().decode(),
      'source_url': self.source_url(),
      'target_url': self.target_url(),
      'include_link': include_link,
    }
    vars = {
      'source': self.preprocess_source(self.source),
      'preview': result.content,
      'description': result.description,
      'webmention_endpoint': util.host_url(self) + '/publish/webmention',
      'state': util.encode_oauth_state(state),
    }
    vars.update(state)
    logging.info('Rendering preview with template vars %s', pprint.pformat(vars))
    return gr_source.creation_result(
      JINJA_ENV.get_template('preview.html').render(**vars))
Example #5
0
  def _render_preview(self, result, include_link=False):
    """Renders a preview CreationResult as HTML.

    Args:
      result: CreationResult
      include_link: boolean

    Returns: CreationResult with the rendered HTML in content
    """
    state = {
      'source_key': self.source.key.urlsafe(),
      'source_url': self.source_url(),
      'target_url': self.target_url(),
      'include_link': include_link,
    }
    vars = {
      'source': self.preprocess_source(self.source),
      'preview': result.content,
      'description': result.description,
      'webmention_endpoint': util.host_url(self) + '/publish/webmention',
      'state': util.encode_oauth_state(state),
    }
    vars.update(state)
    logging.info('Rendering preview with template vars %s', pprint.pformat(vars))
    return gr_source.creation_result(
      JINJA_ENV.get_template('preview.html').render(**vars))
Example #6
0
    def test_expand_target_urls_blacklisted_target(self):
        """RSVP to a domain in the webmention blacklist should not trigger a fetch.
    """
        self.mox.StubOutWithMock(self.source.gr_source,
                                 'create',
                                 use_mock_anything=True)

        self.expect_requests_get(
            'http://foo.com/bar', """
    <article class="h-entry">
     <div class="e-content">
      <span class="p-rsvp" value="yes">yes</span>
      <a class="u-in-reply-to" href="http://fa.ke/homebrew-website-club"></a>
     </div>
      <a class="u-url" href="http://foo.com/bar"></a>
    </article>
    """)

        self.source.gr_source.create({
          'url': 'http://foo.com/bar',
          'verb': 'rsvp-yes',
          'displayName': 'yes',
          'object': [{'url': 'http://fa.ke/homebrew-website-club'}],
          'objectType': 'activity',
          'content': '       <span class="p-rsvp" value="yes">yes</span>       <a class="u-in-reply-to" href="http://fa.ke/homebrew-website-club"></a>      ',
        }, include_link=gr_source.INCLUDE_LINK, ignore_formatting=False). \
        AndReturn(gr_source.creation_result({
          'url': 'http://fake/url',
          'id': 'http://fake/url',
          'content': 'RSVPd yes',
        }))

        self.mox.ReplayAll()
        self.assert_created('')
Example #7
0
    def test_create_like(self):
        self.expect_urlopen(
            'https://api.instagram.com/v1/media/shortcode/ABC123',
            json.dumps({'data': MEDIA}))

        self.expect_urlopen('https://api.instagram.com/v1/media/123_456/likes',
                            '{"meta":{"status":200}}',
                            data='access_token=None')

        self.expect_urlopen(
            'https://api.instagram.com/v1/users/self',
            json.dumps({
                'data': {
                    'id': '8',
                    'username': '******',
                    'full_name': 'Alice',
                    'profile_picture': 'http://alice/picture',
                }
            }))

        # like obj doesn't have url or id prior to publishing
        to_publish = copy.deepcopy(LIKE_OBJS[0])
        del to_publish['id']
        del to_publish['url']

        self.mox.ReplayAll()
        self.assert_equals(source.creation_result(LIKE_OBJS[0]),
                           self.instagram.create(to_publish))
Example #8
0
    def test_create_video_too_big(self):
        self.expect_urlopen('http://foo/xyz.mp4', 'video response')
        self.expect_requests_post(
            'https://up.flickr.com/services/upload',
            data=[('description', 'foo')] + IGNORED_OAUTH_PARAMS,
            files={
                'photo': 'video response'
            }
        ).AndRaise(
            requests.exceptions.ConnectionError(
                socket.timeout(
                    'Request exceeds 10 MiB limit for URL: https://up.flickr.com/services/upload'
                )))
        self.mox.ReplayAll()

        err = 'Sorry, photos and videos must be under 10MB.'
        self.assertEqual(
            source.creation_result(error_plain=err, error_html=err),
            self.flickr.create({
                'objectType': 'note',
                'stream': {
                    'url': 'http://foo/xyz.mp4'
                },
                'url': 'http://foo/xyz',
                'content': 'foo',
            }))
Example #9
0
    def test_create_like(self):
        self.expect_urlopen("https://api.instagram.com/v1/media/shortcode/ABC123", json.dumps({"data": MEDIA}))

        self.expect_urlopen(
            "https://api.instagram.com/v1/media/123_456/likes", '{"meta":{"status":200}}', data="access_token=None"
        )

        self.expect_urlopen(
            "https://api.instagram.com/v1/users/self",
            json.dumps(
                {
                    "data": {
                        "id": "8",
                        "username": "******",
                        "full_name": "Alice",
                        "profile_picture": "http://alice/picture",
                    }
                }
            ),
        )

        # like obj doesn't have url or id prior to publishing
        to_publish = copy.deepcopy(LIKE_OBJS[0])
        del to_publish["id"]
        del to_publish["url"]

        self.mox.ReplayAll()
        self.assert_equals(source.creation_result(LIKE_OBJS[0]), self.instagram.create(to_publish))
Example #10
0
  def test_create_like(self):
    self.expect_urlopen(
      'https://api.instagram.com/v1/media/shortcode/ABC123',
      json.dumps({'data': MEDIA}))

    self.expect_urlopen(
      'https://api.instagram.com/v1/media/123_456/likes',
      '{"meta":{"status":200}}', data='access_token=None')

    self.expect_urlopen(
      'https://api.instagram.com/v1/users/self',
      json.dumps({'data': {
        'id': '8',
        'username': '******',
        'full_name': 'Alice',
        'profile_picture': 'http://alice/picture',
      }}))

    # like obj doesn't have url or id prior to publishing
    to_publish = copy.deepcopy(LIKE_OBJS[0])
    del to_publish['id']
    del to_publish['url']

    self.mox.ReplayAll()
    self.assert_equals(source.creation_result(LIKE_OBJS[0]),
                       self.instagram.create(to_publish))
Example #11
0
  def test_expand_target_urls_fetch_failure(self):
    """Fetching the in-reply-to URL fails, but that shouldn't prevent us
    from publishing the post itself.
    """
    self.mox.StubOutWithMock(self.source.gr_source, 'create',
                             use_mock_anything=True)

    self.expect_requests_get('http://foo.com/bar', """
    <article class="h-entry">
      <a class="u-url" href="http://foo.com/bar"></a>
      <a class="u-in-reply-to" href="http://orig.domain/baz">In reply to</a>
    </article>
    """)

    self.expect_requests_get('http://orig.domain/baz', '', status_code=404)

    self.source.gr_source.create({
      'inReplyTo': [{'url': 'http://orig.domain/baz'}],
      'displayName': 'In reply to',
      'url': 'http://foo.com/bar',
      'objectType': 'comment',
    }, include_link=True, ignore_formatting=False). \
    AndReturn(gr_source.creation_result({
      'url': 'http://fake/url',
      'id': 'http://fake/url',
      'content': 'This is a reply',
    }))

    self.mox.ReplayAll()
    self.assert_created('')
Example #12
0
  def test_expand_target_urls_u_syndication(self):
    """Comment on a post with a u-syndication value
    """
    self.mox.StubOutWithMock(self.source.gr_source, 'create',
                             use_mock_anything=True)

    self.expect_requests_get('http://foo.com/bar', """
    <article class="h-entry">
      <a class="u-url" href="http://foo.com/bar"></a>
      <a class="u-in-reply-to" href="http://orig.domain/baz">In reply to</a>
    </article>
    """)

    self.expect_requests_get('http://orig.domain/baz', """
    <article class="h-entry">
      <span class="p-name e-content">Original post</span>
      <a class="u-syndication" href="https://fa.ke/a/b">syndicated</a>
    </article>
    """)

    self.source.gr_source.create({
      'inReplyTo': [{'url': 'http://orig.domain/baz'},
                    {'url': 'https://fa.ke/a/b'}],
      'displayName': 'In reply to',
      'url': 'http://foo.com/bar',
      'objectType': 'comment',
    }, include_link=True, ignore_formatting=False). \
    AndReturn(gr_source.creation_result({
      'url': 'http://fake/url',
      'id': 'http://fake/url',
      'content': 'This is a reply',
    }))

    self.mox.ReplayAll()
    self.assert_created('')
Example #13
0
  def test_expand_target_urls_no_microformats(self):
    """Publishing a like of a post that has no microformats; should have no
    problems posting the like anyway.
    """
    self.mox.StubOutWithMock(self.source.gr_source, 'create',
                             use_mock_anything=True)

    self.expect_requests_get('http://foo.com/bar', """
    <article class="h-entry">
      <a class="u-url" href="http://foo.com/bar"></a>
      <a class="u-like-of" href="http://orig.domain/baz">liked this</a>
    </article>
    """)

    self.expect_requests_get('http://orig.domain/baz', """
    <article>
      A fantastically well-written article
    </article>
    """)

    self.source.gr_source.create({
      'verb': 'like',
      'displayName': 'liked this',
      'url': 'http://foo.com/bar',
      'object': [{'url': 'http://orig.domain/baz'}],
      'objectType': 'activity',
    }, include_link=True, ignore_formatting=False). \
    AndReturn(gr_source.creation_result({
      'url': 'http://fake/url',
      'id': 'http://fake/url',
      'content': 'liked this',
    }))

    self.mox.ReplayAll()
    self.assert_created('')
Example #14
0
  def test_expand_target_urls_blacklisted_target(self):
    """RSVP to a domain in the webmention blacklist should not trigger a fetch.
    """
    self.mox.StubOutWithMock(self.source.gr_source, 'create',
                             use_mock_anything=True)

    self.expect_requests_get('http://foo.com/bar', """
    <article class="h-entry h-as-rsvp">
     <div class="e-content">
      <span class="p-rsvp" value="yes">yes</span>
      <a class="u-in-reply-to" href="http://fa.ke/homebrew-website-club"></a>
     </div>
      <a class="u-url" href="http://foo.com/bar"></a>
    </article>
    """)

    self.source.gr_source.create({
      'url': 'http://foo.com/bar',
      'verb': 'rsvp-yes',
      'displayName': 'yes',
      'object': [{'url': 'http://fa.ke/homebrew-website-club'}],
      'objectType': 'activity',
      'content': '\n      <span class="p-rsvp" value="yes">yes</span>\n      <a class="u-in-reply-to" href="http://fa.ke/homebrew-website-club"></a>\n     ',
    }, include_link=True, ignore_formatting=False). \
    AndReturn(gr_source.creation_result({
      'url': 'http://fake/url',
      'id': 'http://fake/url',
      'content': 'RSVPd yes',
    }))

    self.mox.ReplayAll()
    self.assert_created('')
Example #15
0
    def test_expand_target_urls_fetch_failure(self):
        """Fetching the in-reply-to URL fails, but that shouldn't prevent us
    from publishing the post itself.
    """
        self.mox.StubOutWithMock(self.source.gr_source,
                                 'create',
                                 use_mock_anything=True)

        self.expect_requests_get(
            'http://foo.com/bar', """
    <article class="h-entry">
      <a class="u-url" href="http://foo.com/bar"></a>
      <a class="u-in-reply-to" href="http://orig.domain/baz">In reply to</a>
    </article>
    """)

        self.expect_requests_get('http://orig.domain/baz', '', status_code=404)

        self.source.gr_source.create({
          'inReplyTo': [{'url': 'http://orig.domain/baz'}],
          'displayName': 'In reply to',
          'url': 'http://foo.com/bar',
          'objectType': 'comment',
        }, include_link=gr_source.INCLUDE_LINK, ignore_formatting=False). \
        AndReturn(gr_source.creation_result({
          'url': 'http://fake/url',
          'id': 'http://fake/url',
          'content': 'This is a reply',
        }))

        self.mox.ReplayAll()
        self.assert_created('')
Example #16
0
    def preview_create(self,
                       obj,
                       include_link=gr_source.OMIT_LINK,
                       ignore_formatting=False):
        if obj.get('verb') == 'like':
            return gr_source.creation_result(
                abort=True,
                error_plain='Cannot publish likes',
                error_html='Cannot publish likes')

        content = self._content_for_create(obj,
                                           ignore_formatting=ignore_formatting)
        if include_link == gr_source.INCLUDE_LINK:
            content += ' - %s' % obj['url']

        content = 'preview of ' + content
        return gr_source.creation_result(description=content)
Example #17
0
  def preview_create(self, obj, include_link=gr_source.OMIT_LINK,
                     ignore_formatting=False):
    if obj.get('verb') == 'like':
      return gr_source.creation_result(
        abort=True, error_plain='Cannot publish likes',
        error_html='Cannot publish likes')

    content = self._content_for_create(obj, ignore_formatting=ignore_formatting)
    if include_link == gr_source.INCLUDE_LINK:
        content += ' - %s' % obj['url']

    content = 'preview of ' + content

    images = self._images(obj)
    if images:
      content += ' with images %s' % ','.join(images)

    return gr_source.creation_result(description=content)
Example #18
0
    def create(self,
               obj,
               include_link=gr_source.OMIT_LINK,
               ignore_formatting=False):
        verb = obj.get('verb')
        type = obj.get('objectType')
        if verb == 'like':
            return gr_source.creation_result(
                abort=True,
                error_plain='Cannot publish likes',
                error_html='Cannot publish likes')
        if 'content' not in obj:
            return gr_source.creation_result(abort=False,
                                             error_plain='No content',
                                             error_html='No content')

        if type == 'comment':
            base_url = self.base_object(obj).get('url')
            if not base_url:
                return gr_source.creation_result(
                    abort=True,
                    error_plain='no %s url to reply to' % self.DOMAIN,
                    error_html='no %s url to reply to' % self.DOMAIN)

        content = self._content_for_create(obj,
                                           ignore_formatting=ignore_formatting)
        if include_link == gr_source.INCLUDE_LINK:
            content += ' - %s' % obj['url']
        ret = {
            'id': 'fake id',
            'url': 'http://fake/url',
            'content': content,
            'granary_message': 'granary message',
        }
        if verb == 'rsvp-yes':
            ret['type'] = 'post'

        images = self._images(obj)
        if images:
            ret['images'] = images

        return gr_source.creation_result(ret)
Example #19
0
  def test_facebook_publish_person_tags(self):
    self.auth_entity = oauth_facebook.FacebookAuth(
      id='auth entity', user_json=json.dumps({'id': '1'}), access_token_str='')
    self.auth_entity.put()

    self.source.key.delete()
    self.source = facebook.FacebookPage.new(
      self.handler, auth_entity=self.auth_entity, features=['publish'],
      domains=['foo.com'])
    self.source.put()

    self.oauth_state['source_key'] = self.source.key.urlsafe()

    input_urls, expected_urls = test_facebook.FacebookPageTest.prepare_person_tags()
    names = [url.strip('/').split('/')[-1].capitalize() for url in input_urls]
    obj = {
      'objectType': 'note',
      'url': 'http://foo.com/bar',
      'content': 'my message',
      'displayName': 'my message\n\nUnknown,444,Username,Inferred,Unknown,My.domain',
      'tags': [{
        'objectType': 'person',
        'url': url,
        'displayName': name,
      } for url, name in zip(expected_urls, names)],
    }
    post_html = """
<article class="h-entry">
<p class="e-content">
my message
</p>
%s
<a href="http://localhost/publish/facebook"></a>
</article>
""" % ','.join('<a class="h-card u-category" href="%s">%s</a>' %
               (url, name) for url, name in zip(input_urls, names))

    for i in range(2):
      self.expect_requests_get('http://foo.com/bar', post_html)

    self.mox.StubOutWithMock(self.source.gr_source, 'create',
                             use_mock_anything=True)
    result = gr_source.creation_result(content={'content': 'my message'})
    self.source.gr_source.create(obj, include_link=True).AndReturn(result)

    self.mox.StubOutWithMock(self.source.gr_source, 'preview_create',
                             use_mock_anything=True)
    self.source.gr_source.preview_create(obj, include_link=False).AndReturn(result)
    self.mox.ReplayAll()

    self.assert_created('my message', interactive=False,
                        target='https://brid.gy/publish/facebook')
    self.assert_success('my message', preview=True,
                        target='https://brid.gy/publish/facebook')
Example #20
0
  def test_create_video_too_big(self):
    self.expect_urlopen('http://foo/xyz.mp4', 'video response')
    self.expect_requests_post(
      'https://up.flickr.com/services/upload',
      data=[('description', 'foo')] + IGNORED_OAUTH_PARAMS,
      files={'photo': 'video response'}
    ).AndRaise(requests.exceptions.ConnectionError(socket.error(
      'Request exceeds 10 MiB limit for URL: https://up.flickr.com/services/upload')))
    self.mox.ReplayAll()

    err = 'Sorry, photos and videos must be under 10MB.'
    self.assert_equals(
      source.creation_result(error_plain=err, error_html=err),
      self.flickr.create({
        'objectType': 'note',
        'stream': {'url': 'http://foo/xyz.mp4'},
        'url': 'http://foo/xyz',
        'content': 'foo',
      }))
Example #21
0
    def test_expand_target_urls_h_event_in_h_feed(self):
        """RSVP to an event is a single element inside an h-feed; we should handle
    it just like a normal post permalink page.
    """
        self.mox.StubOutWithMock(self.source.gr_source,
                                 'create',
                                 use_mock_anything=True)

        self.expect_requests_get(
            'http://foo.com/bar', """
    <article class="h-entry">
      <a class="u-url" href="http://foo.com/bar"></a>
      <a class="u-in-reply-to" href="http://orig.domain/baz"></a>
      <span class="p-rsvp">yes</span>
    </article>
    """)

        self.expect_requests_get(
            'http://orig.domain/baz', """
    <html class="h-feed">
      <article class="h-event">
        <span class="p-name e-content">Original post</span>
        <a class="u-syndication" href="https://fa.ke/a/b">On Fa.ke</a>
      </article>
    </html>
    """)

        self.source.gr_source.create({
          'url': 'http://foo.com/bar',
          'verb': 'rsvp-yes',
          'displayName': 'yes',
          'object': [{'url': 'http://orig.domain/baz'},
                     {'url': 'https://fa.ke/a/b'}],
          'objectType': 'activity',
        }, include_link=gr_source.INCLUDE_LINK, ignore_formatting=False). \
        AndReturn(gr_source.creation_result({
          'url': 'http://fake/url',
          'id': 'http://fake/url',
          'content': 'RSVPd yes',
        }))

        self.mox.ReplayAll()
        self.assert_created('')
Example #22
0
    def test_expand_target_urls_rel_syndication(self):
        """Publishing a like of a post with two rel=syndication values
    """

        self.mox.StubOutWithMock(self.source.gr_source,
                                 'create',
                                 use_mock_anything=True)

        self.expect_requests_get(
            'http://foo.com/bar', """
    <article class="h-entry">
      <a class="u-url" href="http://foo.com/bar"></a>
      <a class="u-like-of" href="http://orig.domain/baz">liked this</a>
    </article>
    """)

        self.expect_requests_get(
            'http://orig.domain/baz', """
    <link rel="syndication" href="https://fa.ke/a/b">
    <link rel="syndication" href="https://flic.kr/c/d">
    <article class="h-entry">
      <span class="p-name e-content">Original post</span>
    </article>
    """)

        self.source.gr_source.create({
          'verb': 'like',
          'displayName': 'liked this',
          'url': 'http://foo.com/bar',
          'object': [{'url': 'http://orig.domain/baz'},
                     {'url': 'https://fa.ke/a/b'},
                     {'url': 'https://flic.kr/c/d'}],
          'objectType': 'activity',
        }, include_link=gr_source.INCLUDE_LINK, ignore_formatting=False). \
        AndReturn(gr_source.creation_result({
          'url': 'http://fake/url',
          'id': 'http://fake/url',
          'content': 'liked this',
        }))

        self.mox.ReplayAll()
        self.assert_created('')
Example #23
0
  def test_create_bridgy_omit_link_maybe_mf2(self):
    """Test that bridgy-omit-link=maybe is parsed properly from mf2
    """
    content = '<data class="p-bridgy-omit-link" value="maybe">foo</data>'
    self.expect_requests_get('http://foo.com/bar', self.post_html % content)

    self.mox.StubOutWithMock(
      self.source.gr_source, 'create', use_mock_anything=True)

    self.source.gr_source.create(
      mox.IgnoreArg(), include_link=gr_source.INCLUDE_IF_TRUNCATED,
      ignore_formatting=False
    ).AndReturn(gr_source.creation_result({
      'url': 'http://fake/url',
      'id': 'http://fake/url',
      'content': 'foo',
    }))

    self.mox.ReplayAll()
    self.assert_created('foo')
Example #24
0
  def test_create_bridgy_omit_link_maybe_query_param(self):
    """Test that ?bridgy_omit_link=maybe query parameter is interpreted
    properly.
    """
    self.expect_requests_get('http://foo.com/bar', self.post_html % 'foo')

    self.mox.StubOutWithMock(
      self.source.gr_source, 'create', use_mock_anything=True)

    self.source.gr_source.create(
      mox.IgnoreArg(), include_link=gr_source.INCLUDE_IF_TRUNCATED,
      ignore_formatting=False
    ).AndReturn(gr_source.creation_result({
      'url': 'http://fake/url',
      'id': 'http://fake/url',
      'content': 'foo',
    }))

    self.mox.ReplayAll()
    self.assert_created('foo', params={'bridgy_omit_link': 'maybe'})
Example #25
0
  def test_expand_target_urls_h_event_in_h_feed(self):
    """RSVP to an event is a single element inside an h-feed; we should handle
    it just like a normal post permalink page.
    """
    self.mox.StubOutWithMock(self.source.gr_source, 'create',
                             use_mock_anything=True)

    self.expect_requests_get('http://foo.com/bar', """
    <article class="h-entry">
      <a class="u-url" href="http://foo.com/bar"></a>
      <a class="u-in-reply-to" href="http://orig.domain/baz"></a>
      <span class="p-rsvp">yes</span>
    </article>
    """)

    self.expect_requests_get('http://orig.domain/baz', """
    <html class="h-feed">
      <article class="h-event">
        <span class="p-name e-content">Original post</span>
        <a class="u-syndication" href="https://fa.ke/a/b">On Fa.ke</a>
      </article>
    </html>
    """)

    self.source.gr_source.create({
      'url': 'http://foo.com/bar',
      'verb': 'rsvp-yes',
      'displayName': 'yes',
      'object': [{'url': 'http://orig.domain/baz'},
                 {'url': 'https://fa.ke/a/b'}],
      'objectType': 'activity',
    }, include_link=True, ignore_formatting=False). \
    AndReturn(gr_source.creation_result({
      'url': 'http://fake/url',
      'id': 'http://fake/url',
      'content': 'RSVPd yes',
    }))

    self.mox.ReplayAll()
    self.assert_created('')
Example #26
0
    def test_expand_target_urls_h_cite(self):
        """Repost a post with a p-syndication h-cite value (syndication
    property is a dict rather than a string)
    """
        self.mox.StubOutWithMock(self.source.gr_source,
                                 'create',
                                 use_mock_anything=True)

        self.expect_requests_get(
            'http://foo.com/bar', """
    <article class="h-entry">
      <a class="u-url" href="http://foo.com/bar"></a>
      <a class="u-repost-of" href="http://orig.domain/baz">reposted this</a>
    </article>
    """)

        self.expect_requests_get(
            'http://orig.domain/baz', """
    <article class="h-entry">
      <span class="p-name e-content">Original post</span>
      <a class="p-syndication h-cite" href="https://fa.ke/a/b">On Fa.ke</a>
    </article>
    """)

        self.source.gr_source.create({
          'verb': 'share',
          'displayName': 'reposted this',
          'url': 'http://foo.com/bar',
          'object': [{'url': 'http://orig.domain/baz'},
                     {'url': 'https://fa.ke/a/b'}],
          'objectType': 'activity',
        }, include_link=gr_source.INCLUDE_LINK, ignore_formatting=False). \
        AndReturn(gr_source.creation_result({
          'url': 'http://fake/url',
          'id': 'http://fake/url',
          'content': 'reposted this',
        }))

        self.mox.ReplayAll()
        self.assert_created('')
Example #27
0
    def test_create_bridgy_omit_link_maybe_query_param(self):
        """Test that ?bridgy_omit_link=maybe query parameter is interpreted
    properly.
    """
        self.expect_requests_get('http://foo.com/bar', self.post_html % 'foo')

        self.mox.StubOutWithMock(self.source.gr_source,
                                 'create',
                                 use_mock_anything=True)

        self.source.gr_source.create(
            mox.IgnoreArg(),
            include_link=gr_source.INCLUDE_IF_TRUNCATED,
            ignore_formatting=False).AndReturn(
                gr_source.creation_result({
                    'url': 'http://fake/url',
                    'id': 'http://fake/url',
                    'content': 'foo',
                }))

        self.mox.ReplayAll()
        self.assert_created('foo', params={'bridgy_omit_link': 'maybe'})
Example #28
0
  def test_expand_target_urls_rel_syndication(self):
    """Publishing a like of a post with two rel=syndication values
    """

    self.mox.StubOutWithMock(self.source.gr_source, 'create',
                             use_mock_anything=True)

    self.expect_requests_get('http://foo.com/bar', """
    <article class="h-entry">
      <a class="u-url" href="http://foo.com/bar"></a>
      <a class="u-like-of" href="http://orig.domain/baz">liked this</a>
    </article>
    """)

    self.expect_requests_get('http://orig.domain/baz', """
    <link rel="syndication" href="https://fa.ke/a/b">
    <link rel="syndication" href="https://flic.kr/c/d">
    <article class="h-entry">
      <span class="p-name e-content">Original post</span>
    </article>
    """)

    self.source.gr_source.create({
      'verb': 'like',
      'displayName': 'liked this',
      'url': 'http://foo.com/bar',
      'object': [{'url': 'http://orig.domain/baz'},
                 {'url': 'https://fa.ke/a/b'},
                 {'url': 'https://flic.kr/c/d'}],
      'objectType': 'activity',
    }, include_link=True, ignore_formatting=False). \
    AndReturn(gr_source.creation_result({
      'url': 'http://fake/url',
      'id': 'http://fake/url',
      'content': 'liked this',
    }))

    self.mox.ReplayAll()
    self.assert_created('')
Example #29
0
    def test_expand_target_urls_u_syndication(self):
        """Comment on a post with a u-syndication value
    """
        self.mox.StubOutWithMock(self.source.gr_source,
                                 'create',
                                 use_mock_anything=True)

        self.expect_requests_get(
            'http://foo.com/bar', """
    <article class="h-entry">
      <a class="u-url" href="http://foo.com/bar"></a>
      <a class="u-in-reply-to" href="http://orig.domain/baz">In reply to</a>
    </article>
    """)

        self.expect_requests_get(
            'http://orig.domain/baz', """
    <article class="h-entry">
      <span class="p-name e-content">Original post</span>
      <a class="u-syndication" href="https://fa.ke/a/b">syndicated</a>
    </article>
    """)

        self.source.gr_source.create({
          'inReplyTo': [{'url': 'http://orig.domain/baz'},
                        {'url': 'https://fa.ke/a/b'}],
          'displayName': 'In reply to',
          'url': 'http://foo.com/bar',
          'objectType': 'comment',
        }, include_link=gr_source.INCLUDE_LINK, ignore_formatting=False). \
        AndReturn(gr_source.creation_result({
          'url': 'http://fake/url',
          'id': 'http://fake/url',
          'content': 'This is a reply',
        }))

        self.mox.ReplayAll()
        self.assert_created('')
Example #30
0
    def test_expand_target_urls_no_microformats(self):
        """Publishing a like of a post that has no microformats; should have no
    problems posting the like anyway.
    """
        self.mox.StubOutWithMock(self.source.gr_source,
                                 'create',
                                 use_mock_anything=True)

        self.expect_requests_get(
            'http://foo.com/bar', """
    <article class="h-entry">
      <a class="u-url" href="http://foo.com/bar"></a>
      <a class="u-like-of" href="http://orig.domain/baz">liked this</a>
    </article>
    """)

        self.expect_requests_get(
            'http://orig.domain/baz', """
    <article>
      A fantastically well-written article
    </article>
    """)

        self.source.gr_source.create({
          'verb': 'like',
          'displayName': 'liked this',
          'url': 'http://foo.com/bar',
          'object': [{'url': 'http://orig.domain/baz'}],
          'objectType': 'activity',
        }, include_link=gr_source.INCLUDE_LINK, ignore_formatting=False). \
        AndReturn(gr_source.creation_result({
          'url': 'http://fake/url',
          'id': 'http://fake/url',
          'content': 'liked this',
        }))

        self.mox.ReplayAll()
        self.assert_created('')
Example #31
0
    def test_create_bridgy_omit_link_maybe_mf2(self):
        """Test that bridgy-omit-link=maybe is parsed properly from mf2
    """
        content = '<data class="p-bridgy-omit-link" value="maybe">foo</data>'
        self.expect_requests_get('http://foo.com/bar',
                                 self.post_html % content)

        self.mox.StubOutWithMock(self.source.gr_source,
                                 'create',
                                 use_mock_anything=True)

        self.source.gr_source.create(
            mox.IgnoreArg(),
            include_link=gr_source.INCLUDE_IF_TRUNCATED,
            ignore_formatting=False).AndReturn(
                gr_source.creation_result({
                    'url': 'http://fake/url',
                    'id': 'http://fake/url',
                    'content': 'foo',
                }))

        self.mox.ReplayAll()
        self.assert_created('foo')
Example #32
0
  def test_expand_target_urls_h_cite(self):
    """Repost a post with a p-syndication h-cite value (syndication
    property is a dict rather than a string)
    """
    self.mox.StubOutWithMock(self.source.gr_source, 'create',
                             use_mock_anything=True)

    self.expect_requests_get('http://foo.com/bar', """
    <article class="h-entry">
      <a class="u-url" href="http://foo.com/bar"></a>
      <a class="u-repost-of" href="http://orig.domain/baz">reposted this</a>
    </article>
    """)

    self.expect_requests_get('http://orig.domain/baz', """
    <article class="h-entry">
      <span class="p-name e-content">Original post</span>
      <a class="p-syndication h-cite" href="https://fa.ke/a/b">On Fa.ke</a>
    </article>
    """)

    self.source.gr_source.create({
      'verb': 'share',
      'displayName': 'reposted this',
      'url': 'http://foo.com/bar',
      'object': [{'url': 'http://orig.domain/baz'},
                 {'url': 'https://fa.ke/a/b'}],
      'objectType': 'activity',
    }, include_link=True, ignore_formatting=False). \
    AndReturn(gr_source.creation_result({
      'url': 'http://fake/url',
      'id': 'http://fake/url',
      'content': 'reposted this',
    }))

    self.mox.ReplayAll()
    self.assert_created('')
Example #33
0
 def preview_delete(self, id):
   return gr_source.creation_result(description='delete %s' % id)
Example #34
0
 def delete(self, id):
   return gr_source.creation_result({
     'url': 'http://fake/url',
     'msg': 'delete %s' % id,
   })
Example #35
0
  def attempt_single_item(self, item):
    """Attempts to preview or publish a single mf2 item.

    Args:
      item: mf2 item dict from mf2py

    Returns:
      a CreationResult object, where content is the string HTTP
      response or None if the source cannot publish this item type.
    """
    props = item.get('properties', {})
    ignore_formatting = self.ignore_formatting()
    if ignore_formatting is None:
      ignore_formatting = 'bridgy-ignore-formatting' in props

    obj = microformats2.json_to_object(item)
    if ignore_formatting:
      prop = microformats2.first_props(props)
      obj['content'] = prop.get('content', {}).get('value').strip()

    # which original post URL to include? if the source URL redirected, use the
    # (pre-redirect) source URL, since it might be a short URL. otherwise, use
    # u-url if it's set. finally, fall back to the actual fetched URL
    if self.source_url() != self.fetched.url:
      obj['url'] = self.source_url()
    elif 'url' not in obj:
      obj['url'] = self.fetched.url
    logging.debug('Converted to ActivityStreams object: %s', json.dumps(obj, indent=2))

    # posts and comments need content
    obj_type = obj.get('objectType')
    if obj_type in ('note', 'article', 'comment'):
      if (not obj.get('content') and not obj.get('summary') and
          not obj.get('displayName')):
        return gr_source.creation_result(
          abort=False,
          error_plain='Could not find content in %s' % self.fetched.url,
          error_html='Could not find <a href="http://microformats.org/">content</a> in %s' % self.fetched.url)

    self.preprocess_activity(obj, ignore_formatting=ignore_formatting)

    omit_link = self.omit_link()
    if omit_link is None:
      omit_link = 'bridgy-omit-link' in props

    if not self.authorize():
      return gr_source.creation_result(abort=True)

    # RIP Facebook comments/likes. https://github.com/snarfed/bridgy/issues/350
    if (isinstance(self.source, FacebookPage) and
        (obj_type == 'comment' or obj.get('verb') == 'like')):
      return gr_source.creation_result(
        abort=True,
        error_plain='Facebook comments and likes are no longer supported. :(',
        error_html='<a href="https://github.com/snarfed/bridgy/issues/350">'
                   'Facebook comments and likes are no longer supported.</a> :(')

    if self.PREVIEW:
      result = self.source.gr_source.preview_create(
        obj, include_link=not omit_link)
      self.entity.published = result.content or result.description
      if not self.entity.published:
        return result  # there was an error
      state = {
        'source_key': self.source.key.urlsafe(),
        'source_url': self.source_url(),
        'target_url': self.target_url(),
        'bridgy_omit_link': omit_link,
      }
      vars = {'source': self.preprocess_source(self.source),
              'preview': result.content,
              'description': result.description,
              'webmention_endpoint': self.request.host_url + '/publish/webmention',
              'state': self.encode_state_parameter(state),
              }
      vars.update(state)
      logging.info('Rendering preview with template vars %s', pprint.pformat(vars))
      return gr_source.creation_result(
        template.render('templates/preview.html', vars))

    else:
      result = self.source.gr_source.create(obj, include_link=not omit_link)
      self.entity.published = result.content
      if not result.content:
        return result  # there was an error
      if 'url' not in self.entity.published:
        self.entity.published['url'] = obj.get('url')
      self.entity.type = self.entity.published.get('type') or models.get_type(obj)
      self.entity.type_label = self.source.TYPE_LABELS.get(self.entity.type)
      self.response.headers['Content-Type'] = 'application/json'
      logging.info('Returning %s', json.dumps(self.entity.published, indent=2))
      return gr_source.creation_result(
        json.dumps(self.entity.published, indent=2))
Example #36
0
    def attempt_single_item(self, item):
        """Attempts to preview or publish a single mf2 item.

    Args:
      item: mf2 item dict from mf2py

    Returns:
      CreationResult
    """
        self.maybe_inject_silo_content(item)
        obj = microformats2.json_to_object(item)

        ignore_formatting = self.ignore_formatting(item)
        if ignore_formatting:
            prop = microformats2.first_props(item.get('properties', {}))
            content = microformats2.get_text(prop.get('content'))
            if content:
                obj['content'] = content.strip()

        # which original post URL to include? in order of preference:
        # 1. rel-shortlink (background: https://github.com/snarfed/bridgy/issues/173)
        # 2. original user-provided URL if it redirected
        # 3. u-url if available
        # 4. actual final fetched URL
        if self.shortlink:
            obj['url'] = self.shortlink
        elif self.source_url() != self.fetched.url:
            obj['url'] = self.source_url()
        elif 'url' not in obj:
            obj['url'] = self.fetched.url
        logging.debug('Converted to ActivityStreams object: %s',
                      json.dumps(obj, indent=2))

        # posts and comments need content
        obj_type = obj.get('objectType')
        if obj_type in ('note', 'article', 'comment'):
            if (not obj.get('content') and not obj.get('summary')
                    and not obj.get('displayName')):
                return gr_source.creation_result(
                    abort=False,
                    error_plain='Could not find content in %s' %
                    self.fetched.url,
                    error_html=
                    'Could not find <a href="http://microformats.org/">content</a> in %s'
                    % self.fetched.url)

        self.preprocess(obj)

        include_link = self.include_link(item)

        if not self.authorize():
            return gr_source.creation_result(abort=True)

        # RIP Facebook comments/likes. https://github.com/snarfed/bridgy/issues/350
        if (isinstance(self.source, FacebookPage)
                and (obj_type == 'comment' or obj.get('verb') == 'like')):
            return gr_source.creation_result(
                abort=True,
                error_plain=
                'Facebook comments and likes are no longer supported. :(',
                error_html=
                '<a href="https://github.com/snarfed/bridgy/issues/350">'
                'Facebook comments and likes are no longer supported.</a> :(')

        if self.PREVIEW:
            result = self.source.gr_source.preview_create(
                obj,
                include_link=include_link,
                ignore_formatting=ignore_formatting)
            self.entity.published = result.content or result.description
            if not self.entity.published:
                return result  # there was an error
            state = {
                'source_key': self.source.key.urlsafe(),
                'source_url': self.source_url(),
                'target_url': self.target_url(),
                'include_link': include_link,
            }
            vars = {
                'source': self.preprocess_source(self.source),
                'preview': result.content,
                'description': result.description,
                'webmention_endpoint':
                self.request.host_url + '/publish/webmention',
                'state': self.encode_state_parameter(state),
            }
            vars.update(state)
            logging.info('Rendering preview with template vars %s',
                         pprint.pformat(vars))
            return gr_source.creation_result(
                template.render('templates/preview.html', vars))

        else:
            result = self.source.gr_source.create(
                obj,
                include_link=include_link,
                ignore_formatting=ignore_formatting)
            self.entity.published = result.content
            if not result.content:
                return result  # there was an error
            if 'url' not in self.entity.published:
                self.entity.published['url'] = obj.get('url')
            self.entity.type = self.entity.published.get(
                'type') or models.get_type(obj)
            self.entity.type_label = self.source.TYPE_LABELS.get(
                self.entity.type)
            self.response.headers['Content-Type'] = 'application/json'
            logging.info('Returning %s',
                         json.dumps(self.entity.published, indent=2))
            self.response.headers['Location'] = self.entity.published[
                'url'].encode('utf-8')
            self.response.status = 201
            return gr_source.creation_result(
                json.dumps(self.entity.published, indent=2))
Example #37
0
  def attempt_single_item(self, item):
    """Attempts to preview or publish a single mf2 item.

    Args:
      item: mf2 item dict from mf2py

    Returns:
      CreationResult
    """
    self.maybe_inject_silo_content(item)
    obj = microformats2.json_to_object(item)

    ignore_formatting = self.ignore_formatting(item)
    if ignore_formatting:
      prop = microformats2.first_props(item.get('properties', {}))
      content = microformats2.get_text(prop.get('content'))
      if content:
        obj['content'] = content.strip()

    # which original post URL to include? in order of preference:
    # 1. rel-shortlink (background: https://github.com/snarfed/bridgy/issues/173)
    # 2. original user-provided URL if it redirected
    # 3. u-url if available
    # 4. actual final fetched URL
    if self.shortlink:
      obj['url'] = self.shortlink
    elif self.source_url() != self.fetched.url:
      obj['url'] = self.source_url()
    elif 'url' not in obj:
      obj['url'] = self.fetched.url
    logging.debug('Converted to ActivityStreams object: %s', json_dumps(obj, indent=2))

    # posts and comments need content
    obj_type = obj.get('objectType')
    if obj_type in ('note', 'article', 'comment'):
      if (not obj.get('content') and not obj.get('summary') and
          not obj.get('displayName')):
        return gr_source.creation_result(
          abort=False,
          error_plain='Could not find content in %s' % self.fetched.url,
          error_html='Could not find <a href="http://microformats.org/">content</a> in %s' % self.fetched.url)

    self.preprocess(obj)

    include_link = self.include_link(item)

    if not self.authorize():
      return gr_source.creation_result(abort=True)

    if self.PREVIEW:
      result = self.source.gr_source.preview_create(
        obj, include_link=include_link, ignore_formatting=ignore_formatting)
      previewed = result.content or result.description
      if self.entity.type == 'preview':
        self.entity.published = previewed
      if not previewed:
        return result  # there was an error
      return self._render_preview(result, include_link=include_link)

    else:
      result = self.source.gr_source.create(
        obj, include_link=include_link, ignore_formatting=ignore_formatting)
      self.entity.published = result.content
      if not result.content:
        return result  # there was an error
      if 'url' not in self.entity.published:
        self.entity.published['url'] = obj.get('url')
      self.entity.type = self.entity.published.get('type') or models.get_type(obj)
      self.response.headers['Content-Type'] = 'application/json'
      logging.info('Returning %s', json_dumps(self.entity.published, indent=2))
      self.response.headers['Location'] = self.entity.published['url']
      self.response.status = 201
      return gr_source.creation_result(
        json_dumps(self.entity.published, indent=2))
Example #38
0
  def attempt_single_item(self, item):
    """Attempts to preview or publish a single mf2 item.

    Args:
      item: mf2 item dict from mf2py

    Returns:
      CreationResult
    """
    self.maybe_inject_silo_content(item)
    obj = microformats2.json_to_object(item)

    ignore_formatting = self.ignore_formatting(item)
    if ignore_formatting:
      prop = microformats2.first_props(item.get('properties', {}))
      content = microformats2.get_text(prop.get('content'))
      if content:
        obj['content'] = content.strip()

    # which original post URL to include? in order of preference:
    # 1. rel-shortlink (background: https://github.com/snarfed/bridgy/issues/173)
    # 2. original user-provided URL if it redirected
    # 3. u-url if available
    # 4. actual final fetched URL
    if self.shortlink:
      obj['url'] = self.shortlink
    elif self.source_url() != self.fetched.url:
      obj['url'] = self.source_url()
    elif 'url' not in obj:
      obj['url'] = self.fetched.url
    logging.debug('Converted to ActivityStreams object: %s', json.dumps(obj, indent=2))

    # posts and comments need content
    obj_type = obj.get('objectType')
    if obj_type in ('note', 'article', 'comment'):
      if (not obj.get('content') and not obj.get('summary') and
          not obj.get('displayName')):
        return gr_source.creation_result(
          abort=False,
          error_plain='Could not find content in %s' % self.fetched.url,
          error_html='Could not find <a href="http://microformats.org/">content</a> in %s' % self.fetched.url)

    self.preprocess(obj)

    omit_link = self.omit_link(item)

    if not self.authorize():
      return gr_source.creation_result(abort=True)

    # RIP Facebook comments/likes. https://github.com/snarfed/bridgy/issues/350
    if (isinstance(self.source, FacebookPage) and
        (obj_type == 'comment' or obj.get('verb') == 'like')):
      return gr_source.creation_result(
        abort=True,
        error_plain='Facebook comments and likes are no longer supported. :(',
        error_html='<a href="https://github.com/snarfed/bridgy/issues/350">'
                   'Facebook comments and likes are no longer supported.</a> :(')

    if self.PREVIEW:
      result = self.source.gr_source.preview_create(
        obj, include_link=not omit_link, ignore_formatting=ignore_formatting)
      self.entity.published = result.content or result.description
      if not self.entity.published:
        return result  # there was an error
      state = {
        'source_key': self.source.key.urlsafe(),
        'source_url': self.source_url(),
        'target_url': self.target_url(),
        'bridgy_omit_link': omit_link,
      }
      vars = {'source': self.preprocess_source(self.source),
              'preview': result.content,
              'description': result.description,
              'webmention_endpoint': self.request.host_url + '/publish/webmention',
              'state': self.encode_state_parameter(state),
              }
      vars.update(state)
      logging.info('Rendering preview with template vars %s', pprint.pformat(vars))
      return gr_source.creation_result(
        template.render('templates/preview.html', vars))

    else:
      result = self.source.gr_source.create(obj, include_link=not omit_link,
                                            ignore_formatting=ignore_formatting)
      self.entity.published = result.content
      if not result.content:
        return result  # there was an error
      if 'url' not in self.entity.published:
        self.entity.published['url'] = obj.get('url')
      self.entity.type = self.entity.published.get('type') or models.get_type(obj)
      self.entity.type_label = self.source.TYPE_LABELS.get(self.entity.type)
      self.response.headers['Content-Type'] = 'application/json'
      logging.info('Returning %s', json.dumps(self.entity.published, indent=2))
      self.response.headers['Location'] = self.entity.published['url'].encode('utf-8')
      self.response.status = 201
      return gr_source.creation_result(
        json.dumps(self.entity.published, indent=2))
Example #39
0
 def delete(self, id):
     return gr_source.creation_result({
         'url': 'http://fake/url',
         'msg': 'delete %s' % id,
     })
Example #40
0
 def preview_delete(self, id):
     return gr_source.creation_result(description='delete %s' % id)
Example #41
0
  def attempt_single_item(self, item):
    """Attempts to preview or publish a single mf2 item.

    Args:
      item: mf2 item dict from mf2py

    Returns:
      CreationResult
    """
    self.maybe_inject_silo_content(item)
    obj = microformats2.json_to_object(item)

    ignore_formatting = self.ignore_formatting(item)
    if ignore_formatting:
      prop = microformats2.first_props(item.get('properties', {}))
      content = microformats2.get_text(prop.get('content'))
      if content:
        obj['content'] = content.strip()

    # which original post URL to include? in order of preference:
    # 1. rel-shortlink (background: https://github.com/snarfed/bridgy/issues/173)
    # 2. original user-provided URL if it redirected
    # 3. u-url if available
    # 4. actual final fetched URL
    if self.shortlink:
      obj['url'] = self.shortlink
    elif self.source_url() != self.fetched.url:
      obj['url'] = self.source_url()
    elif 'url' not in obj:
      obj['url'] = self.fetched.url
    logging.debug('Converted to ActivityStreams object: %s', json.dumps(obj, indent=2))

    # posts and comments need content
    obj_type = obj.get('objectType')
    if obj_type in ('note', 'article', 'comment'):
      if (not obj.get('content') and not obj.get('summary') and
          not obj.get('displayName')):
        return gr_source.creation_result(
          abort=False,
          error_plain='Could not find content in %s' % self.fetched.url,
          error_html='Could not find <a href="http://microformats.org/">content</a> in %s' % self.fetched.url)

    self.preprocess(obj)

    include_link = self.include_link(item)

    if not self.authorize():
      return gr_source.creation_result(abort=True)

    # RIP Facebook.
    # https://github.com/snarfed/bridgy/issues/817
    # https://github.com/snarfed/bridgy/issues/350
    verb = obj.get('verb')
    if isinstance(self.source, FacebookPage):
      return gr_source.creation_result(
        abort=True,
        error_plain='Facebook is no longer supported. So long, and thanks for all the fish!',
        error_html='<a href="https://brid.gy/about#rip-facebook">Facebook is no longer supported. So long, and thanks for all the fish!</a>')

    if self.PREVIEW:
      result = self.source.gr_source.preview_create(
        obj, include_link=include_link, ignore_formatting=ignore_formatting)
      self.entity.published = result.content or result.description
      if not self.entity.published:
        return result  # there was an error
      return self._render_preview(result, include_link=include_link)

    else:
      result = self.source.gr_source.create(
        obj, include_link=include_link, ignore_formatting=ignore_formatting)
      self.entity.published = result.content
      if not result.content:
        return result  # there was an error
      if 'url' not in self.entity.published:
        self.entity.published['url'] = obj.get('url')
      self.entity.type = self.entity.published.get('type') or models.get_type(obj)
      self.response.headers['Content-Type'] = 'application/json'
      logging.info('Returning %s', json.dumps(self.entity.published, indent=2))
      self.response.headers['Location'] = self.entity.published['url'].encode('utf-8')
      self.response.status = 201
      return gr_source.creation_result(
        json.dumps(self.entity.published, indent=2))