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
9 changes: 9 additions & 0 deletions com.woltlab.wcf/templates/articleContentHeaderTitle.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@
<meta itemprop="datePublished" content="{$article->time|date:'c'}">
</li>

{if $articleContent->getReadingTime()}
<li>
{icon name='hourglass'}
<span>{lang minutes=$articleContent->getReadingTime()}wcf.page.readingTime{/lang}</span>
<meta itemprop="timeRequired" content="PT{$articleContent->getReadingTime()}M">
<meta itemprop="wordCount" content="{$articleContent->numberOfWords}">
</li>
{/if}

{if $article->hasLabels}
<li>
{icon name='tags'}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
->defaultValue(0),
NotNullVarchar255DatabaseTableColumn::create('slug')
->defaultValue(''),
IntDatabaseTableColumn::create('numberOfWords')
->notNull()
->defaultValue(0),
]),
PartialDatabaseTable::create('wcf1_label_group')
->columns([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
use wcf\system\user\object\watch\UserObjectWatchHandler;
use wcf\system\version\VersionTracker;
use wcf\system\WCF;
use wcf\util\StringUtil;

/**
* Executes article related actions.
Expand Down Expand Up @@ -110,6 +111,7 @@ public function create()
'slug' => $content['slug'] ?? '',
'teaser' => $content['teaser'],
'content' => $content['content'],
'numberOfWords' => StringUtil::countWords($content['content']),
'imageID' => $content['imageID'],
'teaserImageID' => $content['teaserImageID'],
'metaTitle' => $content['metaTitle'] ?? '',
Expand Down Expand Up @@ -210,6 +212,7 @@ public function update()
'slug' => $isRevert ? $articleContent->slug : ($content['slug'] ?? ''),
'teaser' => $content['teaser'],
'content' => $content['content'],
'numberOfWords' => StringUtil::countWords($content['content']),
'imageID' => ($isRevert) ? $articleContent->imageID : $content['imageID'],
'teaserImageID' => ($isRevert) ? $articleContent->teaserImageID : $content['teaserImageID'],
'metaTitle' => $content['metaTitle'] ?? '',
Expand Down Expand Up @@ -244,6 +247,7 @@ public function update()
'slug' => $isRevert ? '' : ($content['slug'] ?? ''),
'teaser' => $content['teaser'],
'content' => $content['content'],
'numberOfWords' => StringUtil::countWords($content['content']),
'imageID' => ($isRevert) ? null : $content['imageID'],
'teaserImageID' => ($isRevert) ? null : $content['teaserImageID'],
'metaTitle' => $content['metaTitle'] ?? '',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
* @property-read string $metaDescription meta description of the article
* @property-read int $comments number of comments
* @property-read int $attachments number of attachments
* @property-read int $numberOfWords number of words in the content
*
* @extends CollectionDatabaseObject<ArticleContentCollection>
*/
Expand Down Expand Up @@ -281,6 +282,16 @@ public static function getArticleContent(int $articleID, ?int $languageID): ?Art
return null;
}

/**
* Returns the estimated reading time in minutes.
*
* @since 6.3
*/
public function getReadingTime(): int
{
return (int)\ceil($this->numberOfWords / 200);
}

/**
* @since 6.3
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use wcf\system\language\LanguageFactory;
use wcf\system\message\embedded\object\MessageEmbeddedObjectManager;
use wcf\system\tagging\TagEngine;
use wcf\util\StringUtil;

/**
* Builder for creating and updating article contents.
Expand Down Expand Up @@ -69,6 +70,14 @@ public function setTeaser(string $teaser): static
public function setContent(string $content): static
{
$this->properties['content'] = $content;
$this->setNumberOfWords(StringUtil::countWords($content));

return $this;
}

public function setNumberOfWords(int $numberOfWords): static
{
$this->properties['numberOfWords'] = $numberOfWords;

return $this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use wcf\system\language\LanguageFactory;
use wcf\system\tagging\TagEngine;
use wcf\system\WCF;
use wcf\util\StringUtil;

/**
* Imports cms articles.
Expand Down Expand Up @@ -110,6 +111,7 @@ public function import(mixed $oldID, array $data, array $additionalData = [])
'title' => $contentData['title'],
'teaser' => $contentData['teaser'],
'content' => $contentData['content'],
'numberOfWords' => StringUtil::countWords($contentData['content']),
'imageID' => $contentData['imageID'],
'teaserImageID' => $contentData['teaserImageID'],
'metaTitle' => $contentData['metaTitle'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use wcf\system\message\embedded\object\MessageEmbeddedObjectManager;
use wcf\system\search\SearchIndexManager;
use wcf\system\WCF;
use wcf\util\StringUtil;

/**
* Worker implementation for updating articles.
Expand Down Expand Up @@ -128,6 +129,9 @@ public function execute()
$attachmentStatement->execute([$attachmentObjectType->objectTypeID, $articleContent->articleContentID]);
$builder->setAttachments($attachmentStatement->fetchSingleColumn());

// count words
$builder->setNumberOfWords(StringUtil::countWords($articleContent->content ?? ''));

$builder->update();
}

Expand Down
15 changes: 15 additions & 0 deletions wcfsetup/install/files/lib/util/StringUtil.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,21 @@ public static function stripHTML(string $string): string
);
}

/**
* Returns the number of words in the given HTML. Tags are treated as word
* boundaries and words may contain letters of any script.
*
* @since 6.3
*/
public static function countWords(string $html): int
{
$text = \preg_replace('~<!--.*?-->~s', '', $html);
$text = \preg_replace('/<\/?[a-zA-Z](?:[^>"\']+|"[^"]*"|\'[^\']*\')*+>/', ' ', $text);
$text = \html_entity_decode($text, \ENT_QUOTES | \ENT_HTML5, 'UTF-8');

return \preg_match_all('~[\p{L}\p{N}\p{M}]+(?:[\'\x{2019}-][\p{L}\p{N}\p{M}]+)*~u', $text);
}

/**
* Returns false if the given word is forbidden by given word filter.
*/
Expand Down
3 changes: 3 additions & 0 deletions wcfsetup/setup/db/install_com.woltlab.wcf.php
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,9 @@
SmallintDatabaseTableColumn::create('attachments')
->notNull()
->defaultValue(0),
IntDatabaseTableColumn::create('numberOfWords')
->notNull()
->defaultValue(0),
])
->indices([
DatabaseTablePrimaryIndex::create()
Expand Down
Loading