Example #1
0
def process_entity_post(entity, profile, receiving_profile=None):
    """Process an entity of type Post."""
    fid = safe_text(entity.id)
    if not validate_against_old_content(fid, entity, profile):
        return
    values = {
        "fid": fid,
        "text": safe_text_for_markdown(entity.raw_content),
        "author": profile,
        "visibility": Visibility.PUBLIC if entity.public else Visibility.LIMITED,
        "remote_created": safe_make_aware(entity.created_at, "UTC"),
        "service_label": safe_text(entity.provider_display_name) or "",
    }
    values["text"] = _embed_entity_images_to_post(entity._children, values["text"])
    if getattr(entity, "guid", None):
        values["guid"] = safe_text(entity.guid)
    content, created = Content.objects.fed_update_or_create(fid, values)
    _process_mentions(content, entity)
    if created:
        logger.info("Saved Content: %s", content)
    else:
        logger.info("Updated Content: %s", content)
    if content.visibility != Visibility.PUBLIC and receiving_profile:
        content.limited_visibilities.add(receiving_profile)
        logger.info("Added visibility to Post %s to %s", content.fid, receiving_profile.fid)
Example #2
0
def process_entity_comment(entity, profile):
    """Process an entity of type Comment."""
    guid = safe_text(entity.guid)
    if Content.objects.filter(guid=guid, author__user__isnull=False).exists():
        logger.warning("Remote sent comment with guid %s is local! Skipping.",
                       guid)
        return
    try:
        parent = Content.objects.get(guid=entity.target_guid)
    except Content.DoesNotExist:
        logger.warning("No target found for comment: %s", entity)
        return
    values = {
        "text": safe_text_for_markdown(entity.raw_content),
        "author": profile,
        "visibility": parent.visibility,
        "remote_created": safe_make_aware(entity.created_at, "UTC"),
        "parent": parent,
    }
    values["text"] = _embed_entity_images_to_post(entity._children,
                                                  values["text"])
    content, created = Content.objects.update_or_create(guid=guid,
                                                        defaults=values)
    if created:
        logger.info("Saved Content from comment entity: %s", content)
    else:
        logger.info("Updated Content from comment entity: %s", content)
    if parent.is_local:
        # We should relay this to participants we know of
        from socialhome.federate.tasks import forward_relayable
        django_rq.enqueue(forward_relayable, entity, parent.id)
Example #3
0
def process_entity_post(entity, profile, receiving_profile=None):
    """Process an entity of type Post."""
    fid = safe_text(entity.id)
    if not validate_against_old_content(fid, entity, profile):
        return
    values = {
        "fid": fid,
        "text": safe_text_for_markdown(entity.raw_content),
        "author": profile,
        "visibility":
        Visibility.PUBLIC if entity.public else Visibility.LIMITED,
        "remote_created": safe_make_aware(entity.created_at, "UTC"),
        "service_label": safe_text(entity.provider_display_name) or "",
    }
    values["text"] = _embed_entity_images_to_post(entity._children,
                                                  values["text"])
    if getattr(entity, "guid", None):
        values["guid"] = safe_text(entity.guid)
    content, created = Content.objects.fed_update_or_create(fid, values)
    _process_mentions(content, entity)
    if created:
        logger.info("Saved Content: %s", content)
    else:
        logger.info("Updated Content: %s", content)
    if content.visibility != Visibility.PUBLIC and receiving_profile:
        content.limited_visibilities.add(receiving_profile)
        logger.info("Added visibility to Post %s to %s", content.fid,
                    receiving_profile.fid)
Example #4
0
def process_entity_share(entity, profile):
    """Process an entity of type Share."""
    if not entity.entity_type == "Post":
        # TODO: enable shares of replies too
        logger.warning("Ignoring share entity type that is not of type Post")
        return
    try:
        target_content = Content.objects.fed(entity.target_id,
                                             share_of__isnull=True).get()
    except Content.DoesNotExist:
        # Try fetching. If found, process and then try again
        remote_target = retrieve_remote_content(
            entity.target_id,
            guid=entity.target_guid,
            handle=entity.target_handle,
            entity_type=entity.entity_type,
            sender_key_fetcher=sender_key_fetcher,
        )
        if remote_target:
            process_entities([remote_target])
            try:
                target_content = Content.objects.fed(
                    entity.target_id, share_of__isnull=True).get()
            except Content.DoesNotExist:
                logger.warning(
                    "Share target was fetched from remote, but it is still missing locally! Share: %s",
                    entity)
                return
        else:
            logger.warning(
                "No target found for share even after fetching from remote: %s",
                entity)
            return
    values = {
        "text": safe_text_for_markdown(entity.raw_content),
        "author": profile,
        # TODO: ensure visibility constraints depending on shared content?
        "visibility":
        Visibility.PUBLIC if entity.public else Visibility.LIMITED,
        "remote_created": safe_make_aware(entity.created_at, "UTC"),
        "service_label": safe_text(entity.provider_display_name) or "",
    }
    values["text"] = _embed_entity_images_to_post(entity._children,
                                                  values["text"])
    fid = safe_text(entity.id)
    if getattr(entity, "guid", None):
        values["guid"] = safe_text(entity.guid)
    content, created = Content.objects.fed_update_or_create(
        fid, values, extra_lookups={'share_of': target_content})
    _process_mentions(content, entity)
    if created:
        logger.info("Saved share: %s", content)
    else:
        logger.info("Updated share: %s", content)
    # TODO: send participation to the share from the author, if local
    # We probably want that to happen even though our shares are not separate in the stream?
    if target_content.local:
        # We should relay this share entity to participants we know of
        from socialhome.federate.tasks import forward_entity
        django_rq.enqueue(forward_entity, entity, target_content.id)
Example #5
0
def process_entity_comment(entity, profile, receiving_profile=None):
    """Process an entity of type Comment."""
    fid = safe_text(entity.id)
    if not validate_against_old_content(fid, entity, profile):
        return
    try:
        parent = Content.objects.fed(entity.target_id).get()
    except Content.DoesNotExist:
        logger.warning("No target found for comment: %s", entity)
        return
    values = {
        "text": safe_text_for_markdown(entity.raw_content),
        "author": profile,
        "visibility": parent.visibility,
        "remote_created": safe_make_aware(entity.created_at, "UTC"),
        "parent": parent,
    }
    values["text"] = _embed_entity_images_to_post(entity._children,
                                                  values["text"])
    if getattr(entity, "guid", None):
        values["guid"] = safe_text(entity.guid)
    content, created = Content.objects.fed_update_or_create(fid, values)
    _process_mentions(content, entity)
    if created:
        logger.info("Saved Content from comment entity: %s", content)
    else:
        logger.info("Updated Content from comment entity: %s", content)
    if parent.visibility != Visibility.PUBLIC and receiving_profile:
        content.limited_visibilities.add(receiving_profile)
        logger.info("Added visibility to Comment %s to %s", content.uuid,
                    receiving_profile.uuid)
    if parent.local:
        # We should relay this to participants we know of
        from socialhome.federate.tasks import forward_entity
        django_rq.enqueue(forward_entity, entity, parent.id)
Example #6
0
def process_entity_comment(entity, profile, receiving_profile=None):
    """Process an entity of type Comment."""
    fid = safe_text(entity.id)
    if not validate_against_old_content(fid, entity, profile):
        return
    try:
        parent = Content.objects.fed(entity.target_id).get()
    except Content.DoesNotExist:
        logger.warning("No target found for comment: %s", entity)
        return
    values = {
        "text": safe_text_for_markdown(entity.raw_content),
        "author": profile,
        "visibility": parent.visibility,
        "remote_created": safe_make_aware(entity.created_at, "UTC"),
        "parent": parent,
    }
    values["text"] = _embed_entity_images_to_post(entity._children, values["text"])
    if getattr(entity, "guid", None):
        values["guid"] = safe_text(entity.guid)
    content, created = Content.objects.fed_update_or_create(fid, values)
    _process_mentions(content, entity)
    if created:
        logger.info("Saved Content from comment entity: %s", content)
    else:
        logger.info("Updated Content from comment entity: %s", content)
    if parent.visibility != Visibility.PUBLIC and receiving_profile:
        content.limited_visibilities.add(receiving_profile)
        logger.info("Added visibility to Comment %s to %s", content.uuid, receiving_profile.uuid)
    if parent.local:
        # We should relay this to participants we know of
        from socialhome.federate.tasks import forward_entity
        django_rq.enqueue(forward_entity, entity, parent.id)
Example #7
0
def process_entity_comment(entity: Any, profile: Profile):
    """Process an entity of type Comment."""
    fid = safe_text(entity.id)
    if not validate_against_old_content(fid, entity, profile):
        return
    try:
        parent = Content.objects.fed(entity.target_id).get()
    except Content.DoesNotExist:
        logger.warning("No target found for comment: %s", entity)
        return
    root_parent = parent
    if entity.root_target_id:
        try:
            root_parent = Content.objects.fed(entity.root_target_id).get()
        except Content.DoesNotExist:
            pass
    values = {
        "text":
        _embed_entity_images_to_post(
            entity._children, safe_text_for_markdown(entity.raw_content)),
        "author":
        profile,
        "visibility":
        parent.visibility,
        "remote_created":
        safe_make_aware(entity.created_at, "UTC"),
        "parent":
        parent,
        "root_parent":
        root_parent,
    }
    if getattr(entity, "guid", None):
        values["guid"] = safe_text(entity.guid)
    content, created = Content.objects.fed_update_or_create(fid, values)
    _process_mentions(content, entity)
    if created:
        logger.info("Saved Content from comment entity: %s", content)
    else:
        logger.info("Updated Content from comment entity: %s", content)

    # TODO we should respect the visibility of the comment instead here
    if parent.visibility == Visibility.LIMITED:
        if entity._receivers:
            receivers = get_profiles_from_receivers(entity._receivers)
            if len(receivers):
                content.limited_visibilities.set(receivers)
                logger.info("Added visibility to Comment %s to %s",
                            content.fid, receivers)
            else:
                logger.warning(
                    "No local receivers found for limited Comment %s",
                    content.fid)
        else:
            logger.warning("No receivers for limited Comment %s", content.fid)

    if parent.local:
        # We should relay this to participants we know of
        from socialhome.federate.tasks import forward_entity
        django_rq.enqueue(forward_entity, entity, root_parent.id)
Example #8
0
def process_entity_share(entity, profile):
    """Process an entity of type Share."""
    if not entity.entity_type == "Post":
        # TODO: enable shares of replies too
        logger.warning("Ignoring share entity type that is not of type Post")
        return
    try:
        target_content = Content.objects.fed(entity.target_id, share_of__isnull=True).get()
    except Content.DoesNotExist:
        # Try fetching. If found, process and then try again
        remote_target = retrieve_remote_content(
            entity.target_id,
            guid=entity.target_guid,
            handle=entity.target_handle,
            entity_type=entity.entity_type,
            sender_key_fetcher=sender_key_fetcher,
        )
        if remote_target:
            process_entities([remote_target])
            try:
                target_content = Content.objects.fed(entity.target_id, share_of__isnull=True).get()
            except Content.DoesNotExist:
                logger.warning("Share target was fetched from remote, but it is still missing locally! Share: %s",
                               entity)
                return
        else:
            logger.warning("No target found for share even after fetching from remote: %s", entity)
            return
    values = {
        "text": safe_text_for_markdown(entity.raw_content),
        "author": profile,
        # TODO: ensure visibility constraints depending on shared content?
        "visibility": Visibility.PUBLIC if entity.public else Visibility.LIMITED,
        "remote_created": safe_make_aware(entity.created_at, "UTC"),
        "service_label": safe_text(entity.provider_display_name) or "",
    }
    values["text"] = _embed_entity_images_to_post(entity._children, values["text"])
    fid = safe_text(entity.id)
    if getattr(entity, "guid", None):
        values["guid"] = safe_text(entity.guid)
    content, created = Content.objects.fed_update_or_create(fid, values, extra_lookups={'share_of': target_content})
    _process_mentions(content, entity)
    if created:
        logger.info("Saved share: %s", content)
    else:
        logger.info("Updated share: %s", content)
    # TODO: send participation to the share from the author, if local
    # We probably want that to happen even though our shares are not separate in the stream?
    if target_content.local:
        # We should relay this share entity to participants we know of
        from socialhome.federate.tasks import forward_entity
        django_rq.enqueue(forward_entity, entity, target_content.id)
Example #9
0
def process_entity_share(entity, profile):
    """Process an entity of type Share."""
    if not entity.entity_type == "Post":
        # TODO: enable shares of replies too
        logger.warning("Ignoring share entity type that is not of type Post")
        return
    try:
        target_content = Content.objects.get(guid=entity.target_guid,
                                             share_of__isnull=True)
    except Content.DoesNotExist:
        # Try fetching. If found, process and then try again
        remote_target = retrieve_remote_content(
            entity.target_id, sender_key_fetcher=sender_key_fetcher)
        if remote_target:
            process_entities([remote_target])
            try:
                target_content = Content.objects.get(guid=entity.target_guid,
                                                     share_of__isnull=True)
            except Content.DoesNotExist:
                logger.warning(
                    "Share target was fetched from remote, but it is still missing locally! Share: %s",
                    entity)
                return
        else:
            logger.warning(
                "No target found for share even after fetching from remote: %s",
                entity)
            return
    if not target_content.author.handle == entity.target_handle:
        logger.warning(
            "Share target handle is different from the author of locally known shared content!"
        )
        return
    values = {
        "text": safe_text_for_markdown(entity.raw_content),
        "author": profile,
        # TODO: ensure visibility constraints depending on shared content?
        "visibility":
        Visibility.PUBLIC if entity.public else Visibility.LIMITED,
        "remote_created": safe_make_aware(entity.created_at, "UTC"),
        "service_label": safe_text(entity.provider_display_name) or "",
    }
    values["text"] = _embed_entity_images_to_post(entity._children,
                                                  values["text"])
    guid = safe_text(entity.guid)
    content, created = Content.objects.update_or_create(
        guid=guid, share_of=target_content, defaults=values)
    if created:
        logger.info("Saved share: %s", content)
    else:
        logger.info("Updated share: %s", content)
Example #10
0
def process_entity_post(entity, profile):
    """Process an entity of type Post."""
    values = {
        "text": safe_text_for_markdown(entity.raw_content),
        "author": profile,
        "visibility":
        Visibility.PUBLIC if entity.public else Visibility.LIMITED,
        "remote_created": safe_make_aware(entity.created_at, "UTC"),
        "service_label": safe_text(entity.provider_display_name) or "",
    }
    values["text"] = _embed_entity_images_to_post(entity._children,
                                                  values["text"])
    content, created = Content.objects.update_or_create(guid=safe_text(
        entity.guid),
                                                        defaults=values)
    if created:
        logger.info("Saved Content: %s", content)
    else:
        logger.info("Updated Content: %s", content)
Example #11
0
def process_entity_post(entity: Any, profile: Profile):
    """Process an entity of type Post."""
    fid = safe_text(entity.id)
    if not validate_against_old_content(fid, entity, profile):
        return
    values = {
        "fid":
        fid,
        "text":
        _embed_entity_images_to_post(
            entity._children, safe_text_for_markdown(entity.raw_content)),
        "author":
        profile,
        "visibility":
        Visibility.PUBLIC if entity.public else Visibility.LIMITED,
        "remote_created":
        safe_make_aware(entity.created_at, "UTC"),
        "service_label":
        safe_text(entity.provider_display_name) or "",
    }
    extra_lookups = {}
    if getattr(entity, "guid", None):
        values["guid"] = safe_text(entity.guid)
        extra_lookups["guid"] = values["guid"]
    content, created = Content.objects.fed_update_or_create(
        fid, values, extra_lookups=extra_lookups)
    _process_mentions(content, entity)
    if created:
        logger.info("Saved Content: %s", content)
    else:
        logger.info("Updated Content: %s", content)
    if content.visibility == Visibility.LIMITED:
        if entity._receivers:
            receivers = get_profiles_from_receivers(entity._receivers)
            if len(receivers):
                content.limited_visibilities.set(receivers)
                logger.info("Added visibility to Post %s to %s", content.fid,
                            receivers)
            else:
                logger.warning("No local receivers found for limited Post %s",
                               content.fid)
        else:
            logger.warning("No receivers for limited Post %s", content.fid)
Example #12
0
def process_entity_post(entity, profile):
    """Process an entity of type Post."""
    guid = safe_text(entity.guid)
    if Content.objects.filter(guid=guid, author__user__isnull=False).exists():
        logger.warning("Remote sent content with guid %s is local! Skipping.",
                       guid)
        return
    values = {
        "text": safe_text_for_markdown(entity.raw_content),
        "author": profile,
        "visibility":
        Visibility.PUBLIC if entity.public else Visibility.LIMITED,
        "remote_created": safe_make_aware(entity.created_at, "UTC"),
        "service_label": safe_text(entity.provider_display_name) or "",
    }
    values["text"] = _embed_entity_images_to_post(entity._children,
                                                  values["text"])
    content, created = Content.objects.update_or_create(guid=guid,
                                                        defaults=values)
    if created:
        logger.info("Saved Content: %s", content)
    else:
        logger.info("Updated Content: %s", content)
Example #13
0
 def validate_text(self, value):
     """Sanitize text if user is untrusted."""
     user = self.context.get("request").user
     if user.trusted_editor:
         return value
     return safe_text_for_markdown(value)
Example #14
0
 def test_text_with_quotes_survives(self):
     assert safe_text_for_markdown(MARKDOWN_QUOTES_TEXT) == "> foo\n> bar > foo"
Example #15
0
 def test_text_with_html_is_cleaned(self):
     assert safe_text_for_markdown(HTML_TEXT) == '<a href="foo">bar</a><b>cee</b><em>daaa&lt;div&gt;faa&lt' \
                                                      ';/div&gt;</em>'
Example #16
0
 def test_text_with_script_is_cleaned(self):
     assert safe_text_for_markdown(SCRIPT_TEXT) == "&lt;script&gt;console.log&lt;/script&gt;"
Example #17
0
 def clean_text(self):
     """Sanitize text if user is untrusted."""
     if self.user.trusted_editor:
         return self.cleaned_data["text"]
     return safe_text_for_markdown(self.cleaned_data["text"])
Example #18
0
 def test_text_with_quotes_survives(self):
     assert safe_text_for_markdown(
         MARKDOWN_QUOTES_TEXT) == "> foo\n> bar &gt; foo"
Example #19
0
 def test_text_with_script_is_cleaned(self):
     assert safe_text_for_markdown(
         SCRIPT_TEXT) == "&lt;script&gt;console.log&lt;/script&gt;"
Example #20
0
 def test_text_with_markdown_survives(self):
     assert safe_text_for_markdown(MARKDOWN_TEXT) == MARKDOWN_TEXT
Example #21
0
 def test_plain_text_survives(self):
     assert safe_text_for_markdown(PLAIN_TEXT) == PLAIN_TEXT
Example #22
0
 def clean_text(self):
     """Sanitize text if user is untrusted."""
     if self.user.trusted_editor:
         return self.cleaned_data["text"]
     return safe_text_for_markdown(self.cleaned_data["text"])
Example #23
0
 def test_text_with_markdown_code_survives(self):
     assert safe_text_for_markdown(MARKDOWN_CODE_TEXT) == \
            "`\n&lt;script&gt;alert('yup');&lt;/script&gt;\n`\n\n`<script>alert('yup');</script>`\n\n```\n" \
            "<script>alert('yap');</script>\n```"
Example #24
0
 def test_plain_text_survives(self):
     assert safe_text_for_markdown(PLAIN_TEXT) == PLAIN_TEXT
Example #25
0
 def test_text_with_html_is_cleaned(self):
     assert safe_text_for_markdown(HTML_TEXT) == '<a href="foo">bar</a><b>cee</b><em>daaa&lt;div&gt;<span ' \
                                                 'class="jee">faa</span>&lt;/div&gt;</em>'
Example #26
0
 def test_text_with_markdown_survives(self):
     assert safe_text_for_markdown(MARKDOWN_TEXT) == MARKDOWN_TEXT
Example #27
0
 def test_text_with_markdown_code_survives(self):
     assert safe_text_for_markdown(MARKDOWN_CODE_TEXT) == \
            "`\n&lt;script&gt;alert('yup');&lt;/script&gt;\n`\n\n`<script>alert('yup');</script>`\n\n```\n" \
            "<script>alert('yap');</script>\n```"
Example #28
0
 def test_text_with_html_is_cleaned__mention_link_classes_preserved(self):
     assert safe_text_for_markdown(HTML_TEXT_WITH_MENTION_LINK) == \
        '<p><span class="h-card"><a class="u-url mention" href="https://dev.jasonrobinson.me/u/jaywink/"' \
        '>@<span>jaywink</span></a></span> boom</p>'