Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Popover for CMS articles #6155

Merged
merged 1 commit into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions com.woltlab.wcf/templates/article.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@
<span class="articleNavigationArticleContent">
<span class="articleNavigationEntityName">{lang}wcf.article.previousArticle{/lang}</span>
<span class="articleNavigationArticleTitle">
<a href="{$previousArticle->getLink()}" rel="prev" class="articleNavigationArticleLink">
<a href="{$previousArticle->getLink()}" rel="prev" class="articleNavigationArticleLink articleLink" data-object-id="{$previousArticle->getObjectID()}">
{$previousArticle->getTitle()}
</a>
</span>
Expand All @@ -291,7 +291,7 @@
<span class="articleNavigationArticleContent">
<span class="articleNavigationEntityName">{lang}wcf.article.nextArticle{/lang}</span>
<span class="articleNavigationArticleTitle">
<a href="{$nextArticle->getLink()}" rel="next" class="articleNavigationArticleLink">
<a href="{$nextArticle->getLink()}" rel="next" class="articleNavigationArticleLink articleLink" data-object-id="{$nextArticle->getObjectID()}">
{$nextArticle->getTitle()}
</a>
</span>
Expand Down
31 changes: 31 additions & 0 deletions com.woltlab.wcf/templates/articlePopover.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<div class="popover__layout">
{if $article->getTeaserImage()}
<div class="popover__coverPhoto">
<img class="popover__coverPhoto__image" src="{$article->getTeaserImage()->getThumbnailLink('medium')}" alt="">
</div>
{/if}

<div class="popover__header">
{event name='beforeHeader'}

<div class="popover__avatar">
{user object=$article->getUserProfile() type='avatar48' ariaHidden='true' tabindex='-1'}
</div>
<div class="popover__title">
<a href="{$article->getLink()}">{$article->getTitle()}</a>
</div>
<div class="popover__time">
{time time=$article->time}
</div>

{event name='afterHeader'}
</div>

{event name='beforeText'}

<div class="popover__text htmlContent">
{unsafe:$article->getFormattedTeaser()}
</div>

{event name='afterText'}
</div>
2 changes: 1 addition & 1 deletion com.woltlab.wcf/templates/boxArticleList.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<a href="{$boxArticle->getLink()}" aria-hidden="true" tabindex="-1">{unsafe:$boxArticle->getUserProfile()->getAvatar()->getImageTag(24)}</a>

<div class="sidebarItemTitle">
<h3><a href="{$boxArticle->getLink()}">{$boxArticle->getTitle()}</a></h3>
<h3>{anchor object=$boxArticle class='articleLink' title=$boxArticle->getTitle()}</h3>

<small>
{if $boxSortField == 'time'}
Expand Down
28 changes: 28 additions & 0 deletions ts/WoltLabSuite/Core/Api/Articles/GetArticlePopover.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Gets the html code for the rendering of an article popover.
*
* @author Marcel Werk
* @copyright 2001-2025 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @since 6.2
*/

import { prepareRequest } from "WoltLabSuite/Core/Ajax/Backend";
import { ApiResult, apiResultFromError, apiResultFromValue } from "WoltLabSuite/Core/Api/Result";

type Response = {
template: string;
};

export async function getArticlePopover(articleId: number): Promise<ApiResult<string>> {
const url = new URL(`${window.WSC_RPC_API_URL}core/articles/${articleId}/popover`);

let response: Response;
try {
response = (await prepareRequest(url).get().fetchAsJson()) as Response;
} catch (e) {
return apiResultFromError(e);
}

return apiResultFromValue(response.template);
}
16 changes: 16 additions & 0 deletions ts/WoltLabSuite/Core/BootstrapFrontend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import UiPageMenuMainFrontend from "./Ui/Page/Menu/Main/Frontend";
import { whenFirstSeen } from "./LazyLoader";
import { prepareRequest } from "./Ajax/Backend";
import { setup as serviceWorkerSetup } from "./Notification/ServiceWorker";
import { getArticlePopover } from "./Api/Articles/GetArticlePopover";

interface BootstrapOptions {
backgroundQueue: {
Expand Down Expand Up @@ -57,6 +58,20 @@ function setupUserPopover(endpoint: string): void {
});
}

function setupArticlePopover(): void {
whenFirstSeen(".articleLink", () => {
void import("WoltLabSuite/Core/Component/Popover").then(({ setupFor }) => {
setupFor({
endpoint: async (objectId: number) => {
return (await getArticlePopover(objectId)).unwrap();
},
identifier: "com.woltlab.wcf.article",
selector: ".articleLink",
});
});
});
}

declare const COMPILER_TARGET_DEFAULT: boolean;

/**
Expand All @@ -80,6 +95,7 @@ export function setup(options: BootstrapOptions): void {
}

setupUserPopover(options.endpointUserPopover);
setupArticlePopover();

if (options.executeCronjobs !== undefined) {
void prepareRequest(options.executeCronjobs)
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 22 additions & 8 deletions wcfsetup/install/files/js/WoltLabSuite/Core/BootstrapFrontend.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ static function (\wcf\event\acp\dashboard\box\BoxCollecting $event) {
$eventHandler->register(
\wcf\event\endpoint\ControllerCollecting::class,
static function (\wcf\event\endpoint\ControllerCollecting $event) {
$event->register(new \wcf\system\endpoint\controller\core\articles\GetArticlePopover);
$event->register(new \wcf\system\endpoint\controller\core\files\DeleteFile);
$event->register(new \wcf\system\endpoint\controller\core\files\GenerateThumbnails);
$event->register(new \wcf\system\endpoint\controller\core\files\PrepareUpload);
Expand Down
9 changes: 8 additions & 1 deletion wcfsetup/install/files/lib/data/article/Article.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use wcf\data\attachment\GroupedAttachmentList;
use wcf\data\DatabaseObject;
use wcf\data\ILinkableObject;
use wcf\data\IPopoverObject;
use wcf\data\IUserContent;
use wcf\data\object\type\ObjectTypeCache;
use wcf\data\user\UserProfile;
Expand Down Expand Up @@ -38,7 +39,7 @@
* @property-read int $isDeleted is 1 if the article is in trash bin, otherwise 0
* @property-read int $hasLabels is `1` if labels are assigned to the article
*/
class Article extends DatabaseObject implements ILinkableObject, IUserContent
class Article extends DatabaseObject implements ILinkableObject, IPopoverObject, IUserContent
{
/**
* indicates that article is unpublished
Expand Down Expand Up @@ -443,4 +444,10 @@ public function getAttachments(): ?GroupedAttachmentList

return null;
}

#[\Override]
public function getPopoverLinkClass()
{
return 'articleLink';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

namespace wcf\system\endpoint\controller\core\articles;

use Laminas\Diactoros\Response\JsonResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use wcf\data\article\Article;
use wcf\data\article\ViewableArticle;
use wcf\http\Helper;
use wcf\system\endpoint\GetRequest;
use wcf\system\endpoint\IController;
use wcf\system\exception\PermissionDeniedException;
use wcf\system\WCF;

/**
* API endpoint for the rendering of the article popover.
*
* @author Marcel Werk
* @copyright 2001-2025 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @since 6.2
*/
#[GetRequest('/core/articles/{id:\d+}/popover')]
final class GetArticlePopover implements IController
{
#[\Override]
public function __invoke(ServerRequestInterface $request, array $variables): ResponseInterface
{
$article = Helper::fetchObjectFromRequestParameter($variables['id'], Article::class);

$this->assertArticleIsAccessible($article);

return new JsonResponse([
'template' => $this->renderPopover($article),
]);
}

private function assertArticleIsAccessible(Article $article): void
{
if (!$article->canRead()) {
throw new PermissionDeniedException();
}
}

private function renderPopover(Article $article): string
{
return WCF::getTPL()->fetch('articlePopover', 'wcf', [
'article' => ViewableArticle::getArticle($article->articleID),
], true);
}
}
Loading