diff options
Diffstat (limited to 'templates/actor')
| -rw-r--r-- | templates/actor/atom.php | 51 | ||||
| -rw-r--r-- | templates/actor/avatar.php | 5 | ||||
| -rw-r--r-- | templates/actor/profile.php | 83 | ||||
| -rw-r--r-- | templates/actor/profile_block_button.php | 9 | ||||
| -rw-r--r-- | templates/actor/profile_editable.php | 40 | ||||
| -rw-r--r-- | templates/actor/profile_follow_button.php | 20 | ||||
| -rw-r--r-- | templates/actor/profile_page.php | 52 | ||||
| -rw-r--r-- | templates/actor/profile_pending_follow.php | 21 | ||||
| -rw-r--r-- | templates/actor/profile_reset_avatar.php | 3 | ||||
| -rw-r--r-- | templates/actor/rss.php | 46 |
10 files changed, 330 insertions, 0 deletions
diff --git a/templates/actor/atom.php b/templates/actor/atom.php new file mode 100644 index 0000000..8bd40fb --- /dev/null +++ b/templates/actor/atom.php @@ -0,0 +1,51 @@ +<?php +/** @var \Digitigrade\Model\Actor $actor */ +/** @var string $lang */ + +use Digitigrade\GlobalSettings; +use Digitigrade\Model\Note; +use Digitigrade\Model\NotePrivacyScope; + +$selfUri = path_to_uri("/actor/$actor->id/feed/$lang/feed.atom"); +$actorPage = path_to_uri("/@/$actor->handle"); + +echo '<?xml version="1.0" encoding="utf-8"?>'; +?> +<feed xmlns="http://www.w3.org/2005/Atom"> + <id><?= $selfUri ?></id> + <title> + <?= htmlspecialchars($actor->displayName) ?> — <?= GlobalSettings::getInstance()->get('instance.name') ?> + </title> + <updated><?= Note::findAllWithAuthor($actor, 1)[0]->created->format(DateTime::ATOM) ?></updated> + <author> + <name><?= htmlspecialchars($actor->displayName) ?></name> + <uri><?= $actorPage ?></uri> + </author> + <link rel="self" href="<?= $selfUri ?>" /> + <link rel="alternate" type="text/html" href="<?= $actorPage ?>" /> + <generator version="<?= get_version() ?>">Digitigrade</generator> + <?php if (isset($actor->avatar)): ?> + <icon><?= $actor->avatar ?></icon> + <?php endif; ?> + <subtitle><?= __f('user.rss.description', $actor->displayName) ?></subtitle> + + <?php foreach (Note::findAllWithAuthor($actor, limit: 50) as $note): ?> + <?php + /** @var Note $note */ + if ($note->privacy->scope != NotePrivacyScope::PUBLIC || $note->inReplyTo != null) { + continue; + } + ?> + <entry> + <id><?= path_to_uri("/note/$note->id") ?></id> + <title><![CDATA[<?= htmlspecialchars(substr($note->plainContent, 0, 50), double_encode: false) ?>]]></title> + <updated><?= ($note->modified ?? $note->created)->format(DateTime::ATOM) ?></updated> + <link rel="alternate" type="text/html" href="<?= path_to_uri("/@/$actor->handle/note/$note->id") ?>" /> + <content type="html"><![CDATA[<?= $note->getBestContentAsHtml() ?>]]></content> + <?php foreach ($note->attachments as $file): ?> + <link rel="enclosure" title="<?= htmlspecialchars($file->description) ?>" type="<?= $file->type ?>" + href="<?= $file->href ?>" /> + <?php endforeach; ?> + </entry> + <?php endforeach; ?> +</feed>
\ No newline at end of file diff --git a/templates/actor/avatar.php b/templates/actor/avatar.php new file mode 100644 index 0000000..16b529b --- /dev/null +++ b/templates/actor/avatar.php @@ -0,0 +1,5 @@ +<?php if (isset($actor->avatar)): ?> + <img class="avatar" src="<?= $actor->avatar ?>"> +<?php else: ?> + <img class="avatar" src="/static/default-avatar.png"> +<?php endif; ?>
\ No newline at end of file diff --git a/templates/actor/profile.php b/templates/actor/profile.php new file mode 100644 index 0000000..be27c86 --- /dev/null +++ b/templates/actor/profile.php @@ -0,0 +1,83 @@ +<?php +use Digitigrade\Model\UserAccount; +use Digitigrade\UserSettings; + +$user = UserAccount::findByCurrentSession(); +$actorUser = UserAccount::findByLinkedActor($actor); +?> +<div class="fullProfile"> + <div class="basicInfo"> + + <?php if ($addLink ?? false): ?> + <a href="/@/<?= $actor->getFullHandle() ?>"> + <?php endif; ?> + <?php call_template('actor/avatar', ['actor' => $actor]); ?> + <?php if ($addLink ?? false): ?> + </a> + <?php endif; ?> + + <div> + <div class="displayName"><?= htmlspecialchars($actor->displayName) ?></div> + <div class="pronouns"><?= htmlspecialchars($actor->pronouns) ?></div> + <div class="handle">@<?= $actor->getFullHandle() ?></div> + <div class="joinedDate"><?= sprintf(__('user.profile.createdAt'), $actor->created->format('Y-m-d')) ?> + </div> + </div> + + <div class="profileMiniActions"> + <?php call_template('menu_button', [], function () use ($actor, $user, $actorUser) { ?> + <?php if (!$actor->isLocal && isset($actor->homepage)): ?> + <li> + <a href="<?= $actor->homepage ?>" target="_blank"> + <span class="material-symbols open-in-new"></span> + <span><?= __('user.profile.openRemote') ?></span> + </a> + </li> + <?php endif; ?> + <?php if ($actorUser != null && (new UserSettings($actorUser))->getBool('profile.rssEnabled')): ?> + <?php $lang = get_ui_language(); ?> + <li> + <a href="<?= "/actor/$actor->id/feed/$lang/feed.rss" ?>" target="_blank" type="application/rss+xml"> + <span class="material-symbols rss-feed"></span> + <span><?= __('user.profile.feed.rss') ?></span> + </a> + </li> + <li> + <a href="<?= "/actor/$actor->id/feed/$lang/feed.atom" ?>" target="_blank" type="application/atom+xml"> + <span class="material-symbols orbit-outline"></span> + <span><?= __('user.profile.feed.atom') ?></span> + </a> + </li> + <?php endif; ?> + <?php if ($user != null && $user->actor != $actor): ?> + <li><?php call_template('actor/profile_block_button', ['user' => $user, 'actor' => $actor]) ?></li> + <?php endif; ?> + <?php }); ?> + </div> + + </div> + + <?php if (isset($actor->bio) && $actor->bio != '') { ?> + <pre class="bio"><?= htmlspecialchars($actor->bio) ?></pre> + <?php } else { + call_template('placeholder_text', ['message' => __('user.profile.bio.placeholder')]); + } ?> + + <?php if (isset($user) && $actor->id != $user->actor->id): ?> + <div class="profileActions"> + <?php if ($actor->followsPending($user->actor)) + call_template('actor/profile_pending_follow', ['subject' => $actor, 'object' => $user->actor]); ?> + <?php if ($actor->follows($user->actor)): ?> + <?= __('user.profile.followsYou') ?> + <?php endif; ?> + <?php call_template('actor/profile_follow_button', ['actor' => $actor, 'user' => $user]); ?> + </div> + <?php elseif ($actor->id == $user->actor->id): ?> + <div class="profileActions"> + <button class="primary" hx-get="/fragment/profile" hx-target="closest .fullProfile" hx-swap="outerHTML"> + <?= __('form.edit') ?> + </button> + </div> + <?php endif; ?> + +</div>
\ No newline at end of file diff --git a/templates/actor/profile_block_button.php b/templates/actor/profile_block_button.php new file mode 100644 index 0000000..1b71613 --- /dev/null +++ b/templates/actor/profile_block_button.php @@ -0,0 +1,9 @@ +<?php +$blocks = $user->actor->blocks($actor); +$action = $blocks ? 'unblock' : 'block'; +?> +<button hx-confirm="<?= __("user.profile.$action.confirm") ?>" hx-post="/fragment/actor/<?= $actor->id ?>/blockButton" + hx-swap="outerHTML"> + <span class="material-symbols <?= $blocks ? 'remove' : 'block' ?>"></span> + <span><?= __("user.profile.$action") ?></span> +</button>
\ No newline at end of file diff --git a/templates/actor/profile_editable.php b/templates/actor/profile_editable.php new file mode 100644 index 0000000..fc969bd --- /dev/null +++ b/templates/actor/profile_editable.php @@ -0,0 +1,40 @@ +<form class="fullProfile" hx-post="/fragment/profile" hx-swap="outerHTML" enctype="multipart/form-data"> + <div class="basicInfo"> + <?php call_template('actor/avatar', ['actor' => $actor]); ?> + <div> + <div class="row"> + <div class="column"> + <label for="profileDisplayName"><?= __('user.profile.displayName') ?></label> + <input id="profileDisplayName" name="displayName" type="text" required + value="<?= htmlspecialchars($actor->displayName) ?>"> + </div> + <div class="column"> + <label for="profilePronouns"><?= __('user.profile.pronouns') ?></label> + <input id="profilePronouns" name="pronouns" type="text" + value="<?= htmlspecialchars($actor->pronouns) ?>"> + </div> + </div> + <div class="handle">@<?= $actor->getFullHandle() ?></div> + <div class="joinedDate"><?= sprintf(__('user.profile.createdAt'), $actor->created->format('Y-m-d')) ?></div> + </div> + </div> + + <div class="row"> + <label for="profileAvatar"><?= __('user.profile.changeAvatar') ?></label> + <input style="display: inline-block" type="file" id="profileAvatar" name="avatar" accept="image/*"> + <?php call_template('actor/profile_reset_avatar'); ?> + </div> + + <div class="row"> + <label for="profileBio"><?= __('user.profile.bio') ?></label> + <textarea id="profileBio" name="bio"><?= htmlspecialchars($actor->bio ?? '') ?></textarea> + </div> + + <div class="profileActions"> + <button type="button" class="secondary" hx-get="/fragment/actor/<?= $actor->id ?>" + hx-target="closest .fullProfile" hx-swap="outerHTML"> + <?= __('form.discardChanges') ?> + </button> + <button class="primary"><?= __('form.saveChanges') ?></button> + </div> +</form>
\ No newline at end of file diff --git a/templates/actor/profile_follow_button.php b/templates/actor/profile_follow_button.php new file mode 100644 index 0000000..e2b314d --- /dev/null +++ b/templates/actor/profile_follow_button.php @@ -0,0 +1,20 @@ +<?php +use Digitigrade\Model\FollowRelation; +use Digitigrade\Model\FollowRelationStatus; + +$relation = FollowRelation::findByActors($user->actor, $actor); +$state = match ($relation?->status) { + null => $actor->requestToFollow ? 'request' : 'normal', + FollowRelationStatus::PENDING => 'pending', + FollowRelationStatus::ACTIVE => 'active' +}; +?> +<button class="<?= $state == 'active' ? 'secondary' : 'primary' ?>" + hx-post="/fragment/actor/<?= $actor->id ?>/followButton" hx-swap="outerHTML" <?= $state == 'pending' ? 'disabled' : '' ?> hx-disabled-elt="this"> + <?= __(match ($state) { + 'active' => 'user.profile.unfollow.action', + 'pending' => 'user.profile.follow.pending', + 'request' => 'user.profile.requestToFollow.action', + 'normal' => 'user.profile.follow.action' + }) ?> +</button>
\ No newline at end of file diff --git a/templates/actor/profile_page.php b/templates/actor/profile_page.php new file mode 100644 index 0000000..9c11dd6 --- /dev/null +++ b/templates/actor/profile_page.php @@ -0,0 +1,52 @@ +<?php + +use Digitigrade\Model\Note; +use Digitigrade\Model\UserAccount; +use Digitigrade\UserSettings; + +$lang = get_ui_language(); +$actorUser = UserAccount::findByLinkedActor($actor); + +call_template('skeleton', [ + 'pageTitle' => "$actor->displayName - @" . $actor->getFullHandle(), + 'renderTitleHeading' => false, + 'ogTitle' => $actor->displayName, + 'ogDesc' => $actor->bio, + 'ogImage' => $actor->avatar ?? path_to_uri('/static/default-avatar.png'), + 'ogType' => 'profile', + 'ogHandle' => '@' . $actor->getFullHandle(true), + 'headLinks' => ($actorUser != null && (new UserSettings($actorUser))->getBool('profile.rssEnabled')) ? [ + [ + 'rel' => 'alternate', + 'type' => 'application/rss+xml', + 'href' => "/actor/$actor->id/feed/$lang/feed.rss", + 'title' => __('user.profile.feed.rss') + ], + [ + 'rel' => 'alternate', + 'type' => 'application/atom+xml', + 'href' => "/actor/$actor->id/feed/$lang/feed.atom", + 'title' => __('user.profile.feed.atom') + ] + ] : [] +], function () use ($actor) { + $user = UserAccount::findByCurrentSession(); + + call_template('actor/profile', ['actor' => $actor]); + echo '<hr>'; + $notes = Note::findAllWithAuthor($actor, 20); + if (count($notes) == 0) { + call_template('placeholder_text', ['message' => __('user.notes.placeholder')]); + } elseif ($user != null && $user->actor->blocks($actor)) { + call_template('placeholder_text', ['message' => __('user.profile.notesHidden')]); + } else { + foreach ($notes as $n) { + if ($n->isViewableBy($user)) + call_template('note/note', ['note' => $n]); + } + $countNotShown = Note::countWithAuthor($actor) - count($notes); + if ($countNotShown >= 0) { + call_template('placeholder_text', ['count' => $countNotShown]); + } + } +});
\ No newline at end of file diff --git a/templates/actor/profile_pending_follow.php b/templates/actor/profile_pending_follow.php new file mode 100644 index 0000000..2f49fcd --- /dev/null +++ b/templates/actor/profile_pending_follow.php @@ -0,0 +1,21 @@ +<?php if (isset($response)): ?> + + <div class="pendingInfo"> + <?= __("followRequests.$response") ?> + </div> + +<?php else: ?> + + <div class="pendingInfo"> + <?= __('user.profile.pendingFollowsYou') ?> + <button class="primary" hx-post="/fragment/actor/<?= $subject->id ?>/acceptPending" hx-target="closest .pendingInfo" + hx-swap="outerHTML"> + <?= __('followRequests.accept.action') ?> + </button> + <button class="secondary" hx-post="/fragment/actor/<?= $subject->id ?>/rejectPending" + hx-target="closest .pendingInfo" hx-swap="outerHTML"> + <?= __('followRequests.reject.action') ?> + </button> + </div> + +<?php endif; ?>
\ No newline at end of file diff --git a/templates/actor/profile_reset_avatar.php b/templates/actor/profile_reset_avatar.php new file mode 100644 index 0000000..86a3507 --- /dev/null +++ b/templates/actor/profile_reset_avatar.php @@ -0,0 +1,3 @@ +<button type="button" class="secondary inline" hx-post="/fragment/profile/resetAvatar" <?= ($reset ?? false) ? 'disabled' : '' ?>> + <?= __(($reset ?? false) ? 'user.profile.avatarReset' : 'user.profile.resetAvatar') ?> +</button>
\ No newline at end of file diff --git a/templates/actor/rss.php b/templates/actor/rss.php new file mode 100644 index 0000000..03fc4f5 --- /dev/null +++ b/templates/actor/rss.php @@ -0,0 +1,46 @@ +<?php +/** @var \Digitigrade\Model\Actor $actor */ +/** @var string $lang */ + +use Digitigrade\GlobalSettings; +use Digitigrade\Model\Note; +use Digitigrade\Model\NotePrivacyScope; + +// putting this here because otherwise my ide yells about "shorthand <?" +// because it thinks i'm trying to do a php thing instead of xml directive +echo '<?xml version="1.0" encoding="utf-8"?>'; +?> +<rss version="2.0"> + <channel> + <title> + <?= htmlspecialchars($actor->displayName) ?> — <?= GlobalSettings::getInstance()->get('instance.name') ?> + </title> + <link><?= path_to_uri("/@/$actor->handle") ?></link> + <description><?= __f('user.rss.description', htmlspecialchars($actor->displayName)) ?></description> + <language><?= $lang ?></language> + <generator><?= __f('version', get_version()) ?></generator> + <?php if (isset($actor->avatar)): ?> + <image> + <url><?= $actor->avatar ?></url> + </image> + <?php endif; ?> + <?php foreach (Note::findAllWithAuthor($actor, limit: 50) as $note): ?> + <?php + /** @var Note $note */ + if ($note->privacy->scope != NotePrivacyScope::PUBLIC || $note->inReplyTo != null) { + continue; + } + ?> + <item> + <link><?= path_to_uri("/@/$actor->handle/note/$note->id") ?></link> + <guid><?= path_to_uri("/note/$note->id") ?></guid> + <title><![CDATA[<?= htmlspecialchars(substr($note->plainContent, 0, 50), double_encode: false) ?>]]></title> + <description><![CDATA[<?= $note->getBestContentAsHtml() ?>]]></description> + <pubDate><?= $note->created->format(DateTime::RSS) ?></pubDate> + <?php foreach ($note->attachments as $file): ?> + <enclosure url="<?= $file->href ?>" type="<?= $file->type ?>" size="-1" /><!-- size unknown sorry :( --> + <?php endforeach; ?> + </item> + <?php endforeach; ?> + </channel> +</rss>
\ No newline at end of file |
