Skip to content
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
24 changes: 14 additions & 10 deletions com.woltlab.wcf/templates/articleList.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,20 @@
{/capture}

{capture assign='contentInteractionButtons'}
{if $__wcf->user->userID}
<button type="button" class="markAllAsReadButton contentInteractionButton button small jsOnly">{icon name='check'} <span>{lang}wcf.global.button.markAllAsRead{/lang}</span></button>
{if $listView->canMarkAsRead()}
<button type="button" class="markAllArticlesAsReadButton contentInteractionButton button small jsOnly">
{icon name='check'}
<span>{lang}wcf.global.button.markAllAsRead{/lang}</span>
</button>

<script data-relocate="true">
require(['WoltLabSuite/Core/Component/Article/MarkAllArticlesAsRead'], ({ setup }) => {
setup(
document.querySelector('.markAllArticlesAsReadButton'),
document.getElementById('{unsafe:$listView->getID()|encodeJS}_items')
);
});
</script>
{/if}
{/capture}

Expand All @@ -36,14 +48,6 @@
{unsafe:$listView->render()}
</div>

{if $__wcf->user->userID}
<script data-relocate="true">
require(['WoltLabSuite/Core/Ui/Article/MarkAllAsRead'], ({ setup }) => {
setup(document.getElementById('{unsafe:$listView->getID()|encodeJS}_items'));
});
</script>
{/if}

{if $canManageArticles}
{include file='shared_articleAddDialog'}
{/if}
Expand Down
5 changes: 4 additions & 1 deletion com.woltlab.wcf/templates/articleListItems.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
{content}
{if $article->isDeleted}<span class="badge red">{lang}wcf.message.status.deleted{/lang}</span>{/if}
{if !$article->isPublished()}<span class="badge green">{lang}wcf.message.status.disabled{/lang}</span>{/if}
{if $article->isNew()}<span class="badge">{lang}wcf.message.new{/lang}</span>{/if}

{event name='contentItemBadges'}{* deprecated: use badges instead *}
{event name='badges'}
Expand All @@ -51,6 +50,10 @@
{/if}

<h2 class="entryCardList__item__title">
{if $article->isNew()}
{unsafe:$view->renderMarkAsReadButton($article)}
{/if}

<a href="{$article->getLink()}" class="entryCardList__item__link">{$article->getTitle()}</a>
</h2>

Expand Down
24 changes: 14 additions & 10 deletions com.woltlab.wcf/templates/categoryArticleList.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,20 @@
{capture assign='contentInteractionButtons'}
{include file='__userObjectWatchButton' isSubscribed=$category->isSubscribed() objectType='com.woltlab.wcf.article.category' objectID=$category->categoryID}

{if $__wcf->user->userID}
<button type="button" class="markAllAsReadButton contentInteractionButton button small jsOnly">{icon name='check'} <span>{lang}wcf.global.button.markAllAsRead{/lang}</span></button>
{if $listView->canMarkAsRead()}
<button type="button" class="markAllArticlesAsReadButton contentInteractionButton button small jsOnly">
{icon name='check'}
<span>{lang}wcf.global.button.markAllAsRead{/lang}</span>
</button>

<script data-relocate="true">
require(['WoltLabSuite/Core/Component/Article/MarkAllArticlesAsRead'], ({ setup }) => {
setup(
document.querySelector('.markAllArticlesAsReadButton'),
document.getElementById('{unsafe:$listView->getID()|encodeJS}_items')
);
});
</script>
{/if}
{/capture}

Expand All @@ -43,14 +55,6 @@
{unsafe:$listView->render()}
</div>

{if $__wcf->user->userID}
<script data-relocate="true">
require(['WoltLabSuite/Core/Ui/Article/MarkAllAsRead'], ({ setup }) => {
setup(document.getElementById('{unsafe:$listView->getID()|encodeJS}_items'));
});
</script>
{/if}

{if $canManageArticles}
{include file='shared_articleAddDialog'}
{/if}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
/**
* Handles the 'mark as read' action for articles.
*
* @author Marcel Werk
* @copyright 2001-2023 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @author Marcel Werk
* @copyright 2001-2026 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @since 6.3
* @woltlabExcludeBundle tiny
*/

import { showDefaultSuccessSnackbar } from "WoltLabSuite/Core/Component/Snackbar";
import { markAllArticlesAsRead } from "WoltLabSuite/Core/Api/Articles/MarkAllArticlesAsRead";
import { promiseMutex } from "WoltLabSuite/Core/Helper/PromiseMutex";

async function markAllAsRead(listView?: HTMLElement): Promise<void> {
async function markAllAsRead(button: HTMLElement, listView?: HTMLElement): Promise<void> {
await markAllArticlesAsRead();

if (listView !== undefined) {
Expand All @@ -20,16 +21,16 @@ async function markAllAsRead(listView?: HTMLElement): Promise<void> {

document.querySelectorAll(".boxMenu .active .badgeUpdate").forEach((el: HTMLElement) => el.remove());

button.remove();

showDefaultSuccessSnackbar();
}

export function setup(listView?: HTMLElement): void {
document.querySelectorAll(".markAllAsReadButton").forEach((el: HTMLElement) => {
el.addEventListener(
"click",
promiseMutex(async () => {
await markAllAsRead(listView);
}),
);
});
export function setup(button: HTMLElement, listView?: HTMLElement): void {
button.addEventListener(
"click",
promiseMutex(async () => {
await markAllAsRead(button, listView);
}),
);
}

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 @@ -194,6 +194,7 @@ static function (\wcf\event\endpoint\ControllerCollecting $event) {
$event->register(new \wcf\system\endpoint\controller\core\articles\PublishArticle());
$event->register(new \wcf\system\endpoint\controller\core\articles\UnpublishArticle());
$event->register(new \wcf\system\endpoint\controller\core\articles\MarkAllArticlesAsRead());
$event->register(new \wcf\system\endpoint\controller\core\articles\MarkArticleAsRead());
$event->register(new \wcf\system\endpoint\controller\core\articles\contents\GetArticleContentHeaderTitle());
$event->register(new \wcf\system\endpoint\controller\core\attachments\DeleteAttachment());
$event->register(new \wcf\system\endpoint\controller\core\cronjobs\EnableCronjob());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?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\http\Helper;
use wcf\system\endpoint\IController;
use wcf\system\endpoint\PostRequest;
use wcf\system\exception\IllegalLinkException;
use wcf\system\exception\PermissionDeniedException;
use wcf\system\WCF;

/**
* Marks the article with the given ID as read for the current user.
*
* @author Marcel Werk
* @copyright 2001-2026 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @since 6.3
*/
#[PostRequest('/core/articles/{id:\d+}/mark-as-read')]
final class MarkArticleAsRead implements IController
{
#[\Override]
public function __invoke(ServerRequestInterface $request, array $variables): ResponseInterface
{
if (!MODULE_ARTICLE) {
throw new IllegalLinkException();
}

$article = Helper::fetchObjectFromRequestParameter($variables['id'], Article::class);
$this->assertArticleIsAccessible($article);

(new \wcf\command\article\MarkArticleAsRead($article))();

return new JsonResponse([]);
}

private function assertArticleIsAccessible(Article $article): void
{
if (!WCF::getUser()->userID || !$article->canRead()) {
throw new PermissionDeniedException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public function __construct()
$this->setDefaultSortOrder(\ARTICLE_SORT_ORDER);
$this->setCssClassName('entryCardList articleList');
$this->setContainerCssClassName('entryCardList__container');
$this->setMarkAsReadEndpoint('core/articles/%s/mark-as-read');
}

#[\Override]
Expand Down Expand Up @@ -184,4 +185,13 @@ protected function getInitializedEvent(): ArticleListViewInitialized
{
return new ArticleListViewInitialized($this);
}

public function canMarkAsRead(): bool
{
if (!WCF::getUser()->userID) {
return false;
}

return ViewableArticle::getUnreadArticles() > 0;
}
}