Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F21967403
status_view.ex
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
25 KB
Referenced Files
None
Subscribers
None
status_view.ex
View Options
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule
Pleroma.Web.MastodonAPI.StatusView
do
use
Pleroma.Web
,
:view
require
Pleroma.Constants
alias
Pleroma.Activity
alias
Pleroma.HTML
alias
Pleroma.Maps
alias
Pleroma.Object
alias
Pleroma.Repo
alias
Pleroma.User
alias
Pleroma.UserRelationship
alias
Pleroma.Web.CommonAPI
alias
Pleroma.Web.CommonAPI.Utils
alias
Pleroma.Web.MastodonAPI.AccountView
alias
Pleroma.Web.MastodonAPI.PollView
alias
Pleroma.Web.MastodonAPI.StatusView
alias
Pleroma.Web.MediaProxy
alias
Pleroma.Web.PleromaAPI.EmojiReactionController
alias
Pleroma.Web.RichMedia.Card
import
Pleroma.Web.ActivityPub.Visibility
,
only
:
[
get_visibility
:
1
,
visible_for_user?
:
2
]
# This is a naive way to do this, just spawning a process per activity
# to fetch the preview. However it should be fine considering
# pagination is restricted to 40 activities at a time
defp
fetch_rich_media_for_activities
(
activities
)
do
Enum
.
each
(
activities
,
fn
activity
->
Card
.
get_by_activity
(
activity
)
end
)
end
# TODO: Add cached version.
defp
get_replied_to_activities
([]),
do
:
%{}
defp
get_replied_to_activities
(
activities
)
do
activities
|>
Enum
.
map
(
fn
%{
data
:
%{
"type"
=>
"Create"
}}
=
activity
->
object
=
Object
.
normalize
(
activity
,
fetch
:
false
)
object
&&
object
.
data
[
"inReplyTo"
]
!=
""
&&
object
.
data
[
"inReplyTo"
]
_
->
nil
end
)
|>
Enum
.
filter
(
&
&1
)
|>
Activity
.
create_by_object_ap_id_with_object
()
|>
Repo
.
all
()
|>
Enum
.
reduce
(%{},
fn
activity
,
acc
->
object
=
Object
.
normalize
(
activity
,
fetch
:
false
)
if
object
,
do
:
Map
.
put
(
acc
,
object
.
data
[
"id"
],
activity
),
else
:
acc
end
)
end
defp
get_quoted_activities
([]),
do
:
%{}
defp
get_quoted_activities
(
activities
)
do
activities
|>
Enum
.
map
(
fn
%{
data
:
%{
"type"
=>
"Create"
}}
=
activity
->
object
=
Object
.
normalize
(
activity
,
fetch
:
false
)
object
&&
object
.
data
[
"quoteUrl"
]
!=
""
&&
object
.
data
[
"quoteUrl"
]
_
->
nil
end
)
|>
Enum
.
filter
(
&
&1
)
|>
Activity
.
create_by_object_ap_id_with_object
()
|>
Repo
.
all
()
|>
Enum
.
reduce
(%{},
fn
activity
,
acc
->
object
=
Object
.
normalize
(
activity
,
fetch
:
false
)
if
object
,
do
:
Map
.
put
(
acc
,
object
.
data
[
"id"
],
activity
),
else
:
acc
end
)
end
# DEPRECATED This field seems to be a left-over from the StatusNet era.
# If your application uses `pleroma.conversation_id`: this field is deprecated.
# It is currently stubbed instead by doing a CRC32 of the context, and
# clearing the MSB to avoid overflow exceptions with signed integers on the
# different clients using this field (Java/Kotlin code, mostly; see Husky.)
# This should be removed in a future version of Pleroma. Pleroma-FE currently
# depends on this field, as well.
defp
get_context_id
(%{
data
:
%{
"context"
=>
context
}})
when
is_binary
(
context
)
do
import
Bitwise
:erlang
.
crc32
(
context
)
|>
band
(
bnot
(
0x8000
_0000
))
end
defp
get_context_id
(
_
),
do
:
nil
# Check if the user reblogged this status
defp
reblogged?
(
activity
,
%
User
{
ap_id
:
ap_id
})
do
with
%
Object
{
data
:
%{
"announcements"
=>
announcements
}}
when
is_list
(
announcements
)
<-
Object
.
normalize
(
activity
,
fetch
:
false
)
do
ap_id
in
announcements
else
_
->
false
end
end
# False if the user is logged out
defp
reblogged?
(
_activity
,
_user
),
do
:
false
def
render
(
"index.json"
,
opts
)
do
reading_user
=
opts
[
:for
]
# To do: check AdminAPIControllerTest on the reasons behind nil activities in the list
activities
=
Enum
.
filter
(
opts
.
activities
,
&
&1
)
# Start prefetching rich media before doing anything else
fetch_rich_media_for_activities
(
activities
)
replied_to_activities
=
get_replied_to_activities
(
activities
)
quoted_activities
=
get_quoted_activities
(
activities
)
parent_activities
=
activities
|>
Enum
.
filter
(
&
(
&1
.
data
[
"type"
]
==
"Announce"
&&
&1
.
data
[
"object"
]))
|>
Enum
.
map
(
&
Object
.
normalize
(
&1
,
fetch
:
false
)
.
data
[
"id"
])
|>
Activity
.
create_by_object_ap_id
()
|>
Activity
.
with_preloaded_object
(
:left
)
|>
Activity
.
with_preloaded_bookmark
(
reading_user
)
|>
Activity
.
with_set_thread_muted_field
(
reading_user
)
|>
Repo
.
all
()
relationships_opt
=
cond
do
Map
.
has_key?
(
opts
,
:relationships
)
->
opts
[
:relationships
]
is_nil
(
reading_user
)
->
UserRelationship
.
view_relationships_option
(
nil
,
[])
true
->
# Note: unresolved users are filtered out
actors
=
(
activities
++
parent_activities
)
|>
Enum
.
map
(
&
CommonAPI
.
get_user
(
&1
.
data
[
"actor"
],
false
))
|>
Enum
.
filter
(
&
&1
)
UserRelationship
.
view_relationships_option
(
reading_user
,
actors
,
subset
:
:source_mutes
)
end
opts
=
opts
|>
Map
.
put
(
:replied_to_activities
,
replied_to_activities
)
|>
Map
.
put
(
:quoted_activities
,
quoted_activities
)
|>
Map
.
put
(
:parent_activities
,
parent_activities
)
|>
Map
.
put
(
:relationships
,
relationships_opt
)
safe_render_many
(
activities
,
StatusView
,
"show.json"
,
opts
)
end
def
render
(
"show.json"
,
%{
activity
:
%{
data
:
%{
"type"
=>
"Announce"
,
"object"
=>
_object
}}
=
activity
}
=
opts
)
do
user
=
CommonAPI
.
get_user
(
activity
.
data
[
"actor"
])
created_at
=
Utils
.
to_masto_date
(
activity
.
data
[
"published"
])
object
=
Object
.
normalize
(
activity
,
fetch
:
false
)
reblogged_parent_activity
=
if
opts
[
:parent_activities
]
do
Activity.Queries
.
find_by_object_ap_id
(
opts
[
:parent_activities
],
object
.
data
[
"id"
]
)
else
Activity
.
create_by_object_ap_id
(
object
.
data
[
"id"
])
|>
Activity
.
with_preloaded_bookmark
(
opts
[
:for
])
|>
Activity
.
with_set_thread_muted_field
(
opts
[
:for
])
|>
Repo
.
one
()
end
reblog_rendering_opts
=
Map
.
put
(
opts
,
:activity
,
reblogged_parent_activity
)
reblogged
=
render
(
"show.json"
,
reblog_rendering_opts
)
favorited
=
opts
[
:for
]
&&
opts
[
:for
]
.
ap_id
in
(
object
.
data
[
"likes"
]
||
[])
bookmark
=
Activity
.
get_bookmark
(
reblogged_parent_activity
,
opts
[
:for
])
bookmark_folder
=
if
bookmark
!=
nil
do
bookmark
.
folder_id
else
nil
end
mentions
=
activity
.
recipients
|>
Enum
.
map
(
fn
ap_id
->
User
.
get_cached_by_ap_id
(
ap_id
)
end
)
|>
Enum
.
filter
(
&
&1
)
|>
Enum
.
map
(
fn
user
->
AccountView
.
render
(
"mention.json"
,
%{
user
:
user
})
end
)
{
pinned?
,
pinned_at
}
=
pin_data
(
object
,
user
)
%{
id
:
to_string
(
activity
.
id
),
uri
:
object
.
data
[
"id"
],
url
:
object
.
data
[
"id"
],
account
:
AccountView
.
render
(
"show.json"
,
%{
user
:
user
,
for
:
opts
[
:for
]
}),
in_reply_to_id
:
nil
,
in_reply_to_account_id
:
nil
,
reblog
:
reblogged
,
content
:
reblogged
[
:content
]
||
""
,
created_at
:
created_at
,
reblogs_count
:
0
,
replies_count
:
0
,
favourites_count
:
0
,
reblogged
:
reblogged?
(
reblogged_parent_activity
,
opts
[
:for
]),
favourited
:
present?
(
favorited
),
bookmarked
:
present?
(
bookmark
),
muted
:
false
,
pinned
:
pinned?
,
sensitive
:
false
,
spoiler_text
:
""
,
visibility
:
get_visibility
(
activity
),
media_attachments
:
reblogged
[
:media_attachments
]
||
[],
mentions
:
mentions
,
tags
:
reblogged
[
:tags
]
||
[],
application
:
build_application
(
object
.
data
[
"generator"
]),
language
:
nil
,
emojis
:
[],
pleroma
:
%{
local
:
activity
.
local
,
pinned_at
:
pinned_at
,
bookmark_folder
:
bookmark_folder
}
}
end
def
render
(
"show.json"
,
%{
activity
:
%{
data
:
%{
"object"
=>
_object
}}
=
activity
}
=
opts
)
do
object
=
Object
.
normalize
(
activity
,
fetch
:
false
)
user
=
CommonAPI
.
get_user
(
activity
.
data
[
"actor"
])
user_follower_address
=
user
.
follower_address
like_count
=
object
.
data
[
"like_count"
]
||
0
announcement_count
=
object
.
data
[
"announcement_count"
]
||
0
hashtags
=
Object
.
hashtags
(
object
)
sensitive
=
object
.
data
[
"sensitive"
]
||
Enum
.
member?
(
hashtags
,
"nsfw"
)
tags
=
Object
.
tags
(
object
)
tag_mentions
=
tags
|>
Enum
.
filter
(
fn
tag
->
is_map
(
tag
)
and
tag
[
"type"
]
==
"Mention"
end
)
|>
Enum
.
map
(
fn
tag
->
tag
[
"href"
]
end
)
mentions
=
(
object
.
data
[
"to"
]
++
tag_mentions
)
|>
Enum
.
uniq
()
|>
Enum
.
map
(
fn
Pleroma.Constants
.
as_public
()
->
nil
^
user_follower_address
->
nil
ap_id
->
User
.
get_cached_by_ap_id
(
ap_id
)
end
)
|>
Enum
.
filter
(
&
&1
)
|>
Enum
.
map
(
fn
user
->
AccountView
.
render
(
"mention.json"
,
%{
user
:
user
})
end
)
favorited
=
opts
[
:for
]
&&
opts
[
:for
]
.
ap_id
in
(
object
.
data
[
"likes"
]
||
[])
bookmark
=
Activity
.
get_bookmark
(
activity
,
opts
[
:for
])
bookmark_folder
=
if
bookmark
!=
nil
do
bookmark
.
folder_id
else
nil
end
client_posted_this_activity
=
opts
[
:for
]
&&
user
.
id
==
opts
[
:for
]
.
id
expires_at
=
with
true
<-
client_posted_this_activity
,
%
Oban.Job
{
scheduled_at
:
scheduled_at
}
<-
Pleroma.Workers.PurgeExpiredActivity
.
get_expiration
(
activity
.
id
)
do
scheduled_at
else
_
->
nil
end
thread_muted?
=
cond
do
is_nil
(
opts
[
:for
])
->
false
is_boolean
(
activity
.
thread_muted?
)
->
activity
.
thread_muted?
true
->
CommonAPI
.
thread_muted?
(
activity
,
opts
[
:for
])
end
attachment_data
=
object
.
data
[
"attachment"
]
||
[]
attachments
=
render_many
(
attachment_data
,
StatusView
,
"attachment.json"
,
as
:
:attachment
)
created_at
=
Utils
.
to_masto_date
(
object
.
data
[
"published"
])
edited_at
=
with
%{
"updated"
=>
updated
}
<-
object
.
data
,
date
<-
Utils
.
to_masto_date
(
updated
),
true
<-
date
!=
""
do
date
else
_
->
nil
end
reply_to
=
get_reply_to
(
activity
,
opts
)
reply_to_user
=
reply_to
&&
CommonAPI
.
get_user
(
reply_to
.
data
[
"actor"
])
history_len
=
1
+
(
Object.Updater
.
history_for
(
object
.
data
)
|>
Map
.
get
(
"orderedItems"
)
|>
length
())
# See render("history.json", ...) for more details
# Here the implicit index of the current content is 0
chrono_order
=
history_len
-
1
quote_activity
=
get_quote
(
activity
,
opts
)
quote_id
=
case
quote_activity
do
%
Activity
{
id
:
id
}
->
id
_
->
nil
end
quote_post
=
if
visible_for_user?
(
quote_activity
,
opts
[
:for
])
and
opts
[
:show_quote
]
!=
false
do
quote_rendering_opts
=
Map
.
merge
(
opts
,
%{
activity
:
quote_activity
,
show_quote
:
false
})
render
(
"show.json"
,
quote_rendering_opts
)
else
nil
end
content
=
object
|>
render_content
()
content_html
=
content
|>
Activity.HTML
.
get_cached_scrubbed_html_for_activity
(
User
.
html_filter_policy
(
opts
[
:for
]),
activity
,
"mastoapi:content:
#{
chrono_order
}
"
)
content_plaintext
=
content
|>
Activity.HTML
.
get_cached_stripped_html_for_activity
(
activity
,
"mastoapi:content:
#{
chrono_order
}
"
)
summary
=
object
.
data
[
"summary"
]
||
""
card
=
case
Card
.
get_by_activity
(
activity
)
do
%
Card
{}
=
result
->
render
(
"card.json"
,
result
)
_
->
nil
end
url
=
if
user
.
local
do
Pleroma.Web.Router.Helpers
.
o_status_url
(
Pleroma.Web.Endpoint
,
:notice
,
activity
)
else
object
.
data
[
"url"
]
||
object
.
data
[
"external_url"
]
||
object
.
data
[
"id"
]
end
direct_conversation_id
=
with
{
_
,
nil
}
<-
{
:direct_conversation_id
,
opts
[
:direct_conversation_id
]},
{
_
,
true
}
<-
{
:include_id
,
opts
[
:with_direct_conversation_id
]},
{
_
,
%
User
{}
=
for_user
}
<-
{
:for_user
,
opts
[
:for
]}
do
Activity
.
direct_conversation_id
(
activity
,
for_user
)
else
{
:direct_conversation_id
,
participation_id
}
when
is_integer
(
participation_id
)
->
participation_id
_e
->
nil
end
emoji_reactions
=
object
|>
Object
.
get_emoji_reactions
()
|>
EmojiReactionController
.
filter_allowed_users
(
opts
[
:for
],
Map
.
get
(
opts
,
:with_muted
,
false
)
)
|>
Stream
.
map
(
fn
{
emoji
,
users
,
url
}
->
build_emoji_map
(
emoji
,
users
,
url
,
opts
[
:for
])
end
)
|>
Enum
.
to_list
()
# Status muted state (would do 1 request per status unless user mutes are preloaded)
muted
=
thread_muted?
||
UserRelationship
.
exists?
(
get_in
(
opts
,
[
:relationships
,
:user_relationships
]),
:mute
,
opts
[
:for
],
user
,
fn
for_user
,
user
->
User
.
mutes?
(
for_user
,
user
)
end
)
{
pinned?
,
pinned_at
}
=
pin_data
(
object
,
user
)
%{
id
:
to_string
(
activity
.
id
),
uri
:
object
.
data
[
"id"
],
url
:
url
,
account
:
AccountView
.
render
(
"show.json"
,
%{
user
:
user
,
for
:
opts
[
:for
]
}),
in_reply_to_id
:
reply_to
&&
to_string
(
reply_to
.
id
),
in_reply_to_account_id
:
reply_to_user
&&
to_string
(
reply_to_user
.
id
),
reblog
:
nil
,
card
:
card
,
content
:
content_html
,
text
:
opts
[
:with_source
]
&&
get_source_text
(
object
.
data
[
"source"
]),
created_at
:
created_at
,
edited_at
:
edited_at
,
reblogs_count
:
announcement_count
,
replies_count
:
object
.
data
[
"repliesCount"
]
||
0
,
favourites_count
:
like_count
,
reblogged
:
reblogged?
(
activity
,
opts
[
:for
]),
favourited
:
present?
(
favorited
),
bookmarked
:
present?
(
bookmark
),
muted
:
muted
,
pinned
:
pinned?
,
sensitive
:
sensitive
,
spoiler_text
:
summary
,
visibility
:
get_visibility
(
object
),
media_attachments
:
attachments
,
poll
:
render
(
PollView
,
"show.json"
,
object
:
object
,
for
:
opts
[
:for
]),
mentions
:
mentions
,
tags
:
build_tags
(
tags
),
application
:
build_application
(
object
.
data
[
"generator"
]),
language
:
nil
,
emojis
:
build_emojis
(
object
.
data
[
"emoji"
]),
pleroma
:
%{
local
:
activity
.
local
,
conversation_id
:
get_context_id
(
activity
),
context
:
object
.
data
[
"context"
],
in_reply_to_account_acct
:
reply_to_user
&&
reply_to_user
.
nickname
,
quote
:
quote_post
,
quote_id
:
quote_id
,
quote_url
:
object
.
data
[
"quoteUrl"
],
quote_visible
:
visible_for_user?
(
quote_activity
,
opts
[
:for
]),
content
:
%{
"text/plain"
=>
content_plaintext
},
spoiler_text
:
%{
"text/plain"
=>
summary
},
expires_at
:
expires_at
,
direct_conversation_id
:
direct_conversation_id
,
thread_muted
:
thread_muted?
,
emoji_reactions
:
emoji_reactions
,
parent_visible
:
visible_for_user?
(
reply_to
,
opts
[
:for
]),
pinned_at
:
pinned_at
,
quotes_count
:
object
.
data
[
"quotesCount"
]
||
0
,
bookmark_folder
:
bookmark_folder
}
}
end
def
render
(
"show.json"
,
_
)
do
nil
end
def
render
(
"history.json"
,
%{
activity
:
%{
data
:
%{
"object"
=>
_object
}}
=
activity
}
=
opts
)
do
object
=
Object
.
normalize
(
activity
,
fetch
:
false
)
hashtags
=
Object
.
hashtags
(
object
)
user
=
CommonAPI
.
get_user
(
activity
.
data
[
"actor"
])
past_history
=
Object.Updater
.
history_for
(
object
.
data
)
|>
Map
.
get
(
"orderedItems"
)
|>
Enum
.
map
(
&
Map
.
put
(
&1
,
"id"
,
object
.
data
[
"id"
]))
|>
Enum
.
map
(
&
%
Object
{
data
:
&1
,
id
:
object
.
id
})
history
=
[
object
|
past_history
]
# Mastodon expects the original to be at the first
|>
Enum
.
reverse
()
|>
Enum
.
with_index
()
|>
Enum
.
map
(
fn
{
object
,
chrono_order
}
->
%{
# The history is prepended every time there is a new edit.
# In chrono_order, the oldest item is always at 0, and so on.
# The chrono_order is an invariant kept between edits.
chrono_order
:
chrono_order
,
object
:
object
}
end
)
individual_opts
=
opts
|>
Map
.
put
(
:as
,
:item
)
|>
Map
.
put
(
:user
,
user
)
|>
Map
.
put
(
:hashtags
,
hashtags
)
render_many
(
history
,
StatusView
,
"history_item.json"
,
individual_opts
)
end
def
render
(
"history_item.json"
,
%{
activity
:
activity
,
user
:
user
,
item
:
%{
object
:
object
,
chrono_order
:
chrono_order
},
hashtags
:
hashtags
}
=
opts
)
do
sensitive
=
object
.
data
[
"sensitive"
]
||
Enum
.
member?
(
hashtags
,
"nsfw"
)
attachment_data
=
object
.
data
[
"attachment"
]
||
[]
attachments
=
render_many
(
attachment_data
,
StatusView
,
"attachment.json"
,
as
:
:attachment
)
created_at
=
Utils
.
to_masto_date
(
object
.
data
[
"updated"
]
||
object
.
data
[
"published"
])
content
=
object
|>
render_content
()
content_html
=
content
|>
Activity.HTML
.
get_cached_scrubbed_html_for_activity
(
User
.
html_filter_policy
(
opts
[
:for
]),
activity
,
"mastoapi:content:
#{
chrono_order
}
"
)
summary
=
object
.
data
[
"summary"
]
||
""
%{
account
:
AccountView
.
render
(
"show.json"
,
%{
user
:
user
,
for
:
opts
[
:for
]
}),
content
:
content_html
,
sensitive
:
sensitive
,
spoiler_text
:
summary
,
created_at
:
created_at
,
media_attachments
:
attachments
,
emojis
:
build_emojis
(
object
.
data
[
"emoji"
]),
poll
:
render
(
PollView
,
"show.json"
,
object
:
object
,
for
:
opts
[
:for
])
}
end
def
render
(
"source.json"
,
%{
activity
:
%{
data
:
%{
"object"
=>
_object
}}
=
activity
}
=
_opts
)
do
object
=
Object
.
normalize
(
activity
,
fetch
:
false
)
%{
id
:
activity
.
id
,
text
:
get_source_text
(
Map
.
get
(
object
.
data
,
"source"
,
""
)),
spoiler_text
:
Map
.
get
(
object
.
data
,
"summary"
,
""
),
content_type
:
get_source_content_type
(
object
.
data
[
"source"
])
}
end
def
render
(
"card.json"
,
%
Card
{
fields
:
rich_media
})
do
page_url_data
=
URI
.
parse
(
rich_media
[
"url"
])
page_url
=
page_url_data
|>
to_string
image_url
=
proxied_url
(
rich_media
[
"image"
],
page_url_data
)
audio_url
=
proxied_url
(
rich_media
[
"audio"
],
page_url_data
)
video_url
=
proxied_url
(
rich_media
[
"video"
],
page_url_data
)
%{
type
:
"link"
,
provider_name
:
page_url_data
.
host
,
provider_url
:
page_url_data
.
scheme
<>
"://"
<>
page_url_data
.
host
,
url
:
page_url
,
image
:
image_url
,
image_description
:
rich_media
[
"image:alt"
]
||
""
,
title
:
rich_media
[
"title"
]
||
""
,
description
:
rich_media
[
"description"
]
||
""
,
pleroma
:
%{
opengraph
:
rich_media
|>
Maps
.
put_if_present
(
"image"
,
image_url
)
|>
Maps
.
put_if_present
(
"audio"
,
audio_url
)
|>
Maps
.
put_if_present
(
"video"
,
video_url
)
}
}
end
def
render
(
"card.json"
,
_
),
do
:
nil
def
render
(
"attachment.json"
,
%{
attachment
:
attachment
})
do
[
attachment_url
|
_
]
=
attachment
[
"url"
]
media_type
=
attachment_url
[
"mediaType"
]
||
attachment_url
[
"mimeType"
]
||
"image"
href
=
attachment_url
[
"href"
]
|>
MediaProxy
.
url
()
href_preview
=
attachment_url
[
"href"
]
|>
MediaProxy
.
preview_url
()
meta
=
render
(
"attachment_meta.json"
,
%{
attachment
:
attachment
})
type
=
cond
do
String
.
contains?
(
media_type
,
"image"
)
->
"image"
String
.
contains?
(
media_type
,
"video"
)
->
"video"
String
.
contains?
(
media_type
,
"audio"
)
->
"audio"
true
->
"unknown"
end
attachment_id
=
with
{
_
,
ap_id
}
when
is_binary
(
ap_id
)
<-
{
:ap_id
,
attachment
[
"id"
]},
{
_
,
%
Object
{
data
:
_object_data
,
id
:
object_id
}}
<-
{
:object
,
Object
.
get_by_ap_id
(
ap_id
)}
do
to_string
(
object_id
)
else
_
->
<<
hash_id
::
signed
-
32
,
_rest
::
binary
>>
=
:crypto
.
hash
(
:md5
,
href
)
to_string
(
attachment
[
"id"
]
||
hash_id
)
end
description
=
if
attachment
[
"summary"
]
do
HTML
.
strip_tags
(
attachment
[
"summary"
])
else
attachment
[
"name"
]
end
name
=
if
attachment
[
"summary"
],
do
:
attachment
[
"name"
]
pleroma
=
%{
mime_type
:
media_type
}
|>
Maps
.
put_if_present
(
:name
,
name
)
%{
id
:
attachment_id
,
url
:
href
,
remote_url
:
href
,
preview_url
:
href_preview
,
text_url
:
href
,
type
:
type
,
description
:
description
,
pleroma
:
pleroma
,
blurhash
:
attachment
[
"blurhash"
]
}
|>
Maps
.
put_if_present
(
:meta
,
meta
)
end
def
render
(
"attachment_meta.json"
,
%{
attachment
:
%{
"url"
=>
[%{
"width"
=>
width
,
"height"
=>
height
}
|
_
]}
})
when
is_integer
(
width
)
and
is_integer
(
height
)
do
%{
original
:
%{
width
:
width
,
height
:
height
,
aspect
:
width
/
height
}
}
end
def
render
(
"attachment_meta.json"
,
_
),
do
:
nil
def
render
(
"context.json"
,
%{
activity
:
activity
,
activities
:
activities
,
user
:
user
})
do
%{
ancestors
:
ancestors
,
descendants
:
descendants
}
=
activities
|>
Enum
.
reverse
()
|>
Enum
.
group_by
(
fn
%{
id
:
id
}
->
if
id
<
activity
.
id
,
do
:
:ancestors
,
else
:
:descendants
end
)
|>
Map
.
put_new
(
:ancestors
,
[])
|>
Map
.
put_new
(
:descendants
,
[])
%{
ancestors
:
render
(
"index.json"
,
for
:
user
,
activities
:
ancestors
,
as
:
:activity
),
descendants
:
render
(
"index.json"
,
for
:
user
,
activities
:
descendants
,
as
:
:activity
)
}
end
def
get_reply_to
(
activity
,
%{
replied_to_activities
:
replied_to_activities
})
do
object
=
Object
.
normalize
(
activity
,
fetch
:
false
)
with
nil
<-
replied_to_activities
[
object
.
data
[
"inReplyTo"
]]
do
# If user didn't participate in the thread
Activity
.
get_in_reply_to_activity
(
activity
)
end
end
def
get_reply_to
(%{
data
:
%{
"object"
=>
_object
}}
=
activity
,
_
)
do
object
=
Object
.
normalize
(
activity
,
fetch
:
false
)
if
object
.
data
[
"inReplyTo"
]
&&
object
.
data
[
"inReplyTo"
]
!=
""
do
Activity
.
get_create_by_object_ap_id
(
object
.
data
[
"inReplyTo"
])
else
nil
end
end
def
get_quote
(
activity
,
%{
quoted_activities
:
quoted_activities
})
do
object
=
Object
.
normalize
(
activity
,
fetch
:
false
)
with
nil
<-
quoted_activities
[
object
.
data
[
"quoteUrl"
]]
do
# For when a quote post is inside an Announce
Activity
.
get_create_by_object_ap_id_with_object
(
object
.
data
[
"quoteUrl"
])
end
end
def
get_quote
(%{
data
:
%{
"object"
=>
_object
}}
=
activity
,
_
)
do
object
=
Object
.
normalize
(
activity
,
fetch
:
false
)
if
object
.
data
[
"quoteUrl"
]
&&
object
.
data
[
"quoteUrl"
]
!=
""
do
Activity
.
get_create_by_object_ap_id
(
object
.
data
[
"quoteUrl"
])
else
nil
end
end
def
render_content
(%{
data
:
%{
"name"
=>
name
}}
=
object
)
when
not
is_nil
(
name
)
and
name
!=
""
do
url
=
object
.
data
[
"url"
]
||
object
.
data
[
"id"
]
"<p><a href=\"
#{
url
}
\">
#{
name
}
</a></p>
#{
object
.
data
[
"content"
]
}
"
end
def
render_content
(
object
),
do
:
object
.
data
[
"content"
]
||
""
@doc
"""
Builds a dictionary tags.
##
Examples
iex> Pleroma.Web.MastodonAPI.StatusView.build_tags(["fediverse", "nextcloud"])
[{"name": "fediverse", "url": "/tag/fediverse"},
{"name": "nextcloud", "url": "/tag/nextcloud"}]
"""
@spec
build_tags
(
list
(
any
()))
::
list
(
map
())
def
build_tags
(
object_tags
)
when
is_list
(
object_tags
)
do
object_tags
|>
Enum
.
filter
(
&
is_binary
/
1
)
|>
Enum
.
map
(
&
%{
name
:
&1
,
url
:
"
#{
Pleroma.Web.Endpoint
.
url
()
}
/tag/
#{
URI
.
encode
(
&1
)
}
"
})
end
def
build_tags
(
_
),
do
:
[]
@doc
"""
Builds list emojis.
Arguments: `nil` or list tuple of name and url.
Returns list emojis.
##
Examples
iex> Pleroma.Web.MastodonAPI.StatusView.build_emojis([{"2hu", "corndog.png"}])
[%{shortcode: "2hu", static_url: "corndog.png", url: "corndog.png", visible_in_picker: false}]
"""
@spec
build_emojis
(
nil
|
list
(
tuple
()))
::
list
(
map
())
def
build_emojis
(
nil
),
do
:
[]
def
build_emojis
(
emojis
)
do
emojis
|>
Enum
.
map
(
fn
{
name
,
url
}
->
name
=
HTML
.
strip_tags
(
name
)
url
=
url
|>
HTML
.
strip_tags
()
|>
MediaProxy
.
url
()
%{
shortcode
:
name
,
url
:
url
,
static_url
:
url
,
visible_in_picker
:
false
}
end
)
end
defp
present?
(
nil
),
do
:
false
defp
present?
(
false
),
do
:
false
defp
present?
(
_
),
do
:
true
defp
pin_data
(%
Object
{
data
:
%{
"id"
=>
object_id
}},
%
User
{
pinned_objects
:
pinned_objects
})
do
if
pinned_at
=
pinned_objects
[
object_id
]
do
{
true
,
Utils
.
to_masto_date
(
pinned_at
)}
else
{
false
,
nil
}
end
end
defp
build_emoji_map
(
emoji
,
users
,
url
,
current_user
)
do
%{
name
:
Pleroma.Web.PleromaAPI.EmojiReactionView
.
emoji_name
(
emoji
,
url
),
count
:
length
(
users
),
url
:
MediaProxy
.
url
(
url
),
me
:
!!
(
current_user
&&
current_user
.
ap_id
in
users
),
account_ids
:
Enum
.
map
(
users
,
fn
user
->
User
.
get_cached_by_ap_id
(
user
)
.
id
end
)
}
end
@spec
build_application
(
map
()
|
nil
)
::
map
()
|
nil
defp
build_application
(%{
"type"
=>
_type
,
"name"
=>
name
,
"url"
=>
url
}),
do
:
%{
name
:
name
,
website
:
url
}
defp
build_application
(
_
),
do
:
nil
# Workaround for Elixir issue #10771
# Avoid applying URI.merge unless necessary
# TODO: revert to always attempting URI.merge(image_url_data, page_url_data)
# when Elixir 1.12 is the minimum supported version
@spec
build_image_url
(
struct
()
|
nil
,
struct
())
::
String
.
t
()
|
nil
defp
build_image_url
(
%
URI
{
scheme
:
image_scheme
,
host
:
image_host
}
=
image_url_data
,
%
URI
{}
=
_page_url_data
)
when
not
is_nil
(
image_scheme
)
and
not
is_nil
(
image_host
)
do
image_url_data
|>
to_string
end
defp
build_image_url
(%
URI
{}
=
image_url_data
,
%
URI
{}
=
page_url_data
)
do
URI
.
merge
(
page_url_data
,
image_url_data
)
|>
to_string
end
defp
get_source_text
(%{
"content"
=>
content
}
=
_source
)
do
content
end
defp
get_source_text
(
source
)
when
is_binary
(
source
)
do
source
end
defp
get_source_text
(
_
)
do
""
end
defp
get_source_content_type
(%{
"mediaType"
=>
type
}
=
_source
)
do
type
end
defp
get_source_content_type
(
_source
)
do
Utils
.
get_content_type
(
nil
)
end
defp
proxied_url
(
url
,
page_url_data
)
do
if
is_binary
(
url
)
do
build_image_url
(
URI
.
parse
(
url
),
page_url_data
)
|>
MediaProxy
.
url
()
else
nil
end
end
end
File Metadata
Details
Attached
Mime Type
text/x-ruby
Expires
Sun, Dec 28, 3:23 AM (13 h, 20 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
829274
Default Alt Text
status_view.ex (25 KB)
Attached To
Mode
rPUBE pleroma-upstream
Attached
Detach File
Event Timeline
Log In to Comment