Skip to content

Commit 0b6bb08

Browse files
committed
Merge branch '6.2' into 6.3
2 parents a8a300b + 901367b commit 0b6bb08

6 files changed

Lines changed: 164 additions & 56 deletions

File tree

.github/workflows/xml.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: XML
2+
3+
on:
4+
push:
5+
branches:
6+
- '6.0'
7+
- '6.1'
8+
- '6.2'
9+
- '6.3'
10+
paths:
11+
- '**.xml'
12+
pull_request:
13+
paths:
14+
- '**.xml'
15+
16+
permissions:
17+
contents: read
18+
19+
jobs:
20+
syntax:
21+
name: Check Syntax
22+
runs-on: ubuntu-latest
23+
steps:
24+
- name: Install xmllint
25+
run: |
26+
sudo apt-get update
27+
sudo apt-get install -y --no-install-recommends libxml2-utils
28+
- name: Checkout
29+
uses: actions/checkout@v6
30+
- name: Run xmllint
31+
run: |
32+
! find . -type f -name '*.xml' -exec xmllint --noout '{}' \; 2>&1 | grep -v '^$'

files/lib/system/exporter/IPB4xExporter.class.php

Lines changed: 72 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace wcf\system\exporter;
44

5+
use blog\data\blog\Blog;
56
use wbb\data\board\Board;
67
use wcf\data\like\Like;
78
use wcf\data\user\group\UserGroup;
@@ -394,8 +395,10 @@ public function exportUserOptions($offset, $limit)
394395
while ($row = $statement->fetchArray()) {
395396
$data = [
396397
'categoryName' => 'profile.personal',
397-
'optionType' => 'textarea',
398+
'optionType' => $row['pf_type'] === 'Editor' ? 'message' : 'textarea',
398399
'askDuringRegistration' => $row['pf_show_on_reg'],
400+
'editable' => 3,
401+
'visible' => 15,
399402
];
400403

401404
ImportHandler::getInstance()
@@ -659,7 +662,7 @@ public function exportConversations($offset, $limit)
659662
'time' => $row['mt_date'],
660663
'userID' => $row['mt_starter_id'] ?: null,
661664
'username' => $row['mt_is_system'] ? 'System' : ($row['name'] ?: ''),
662-
'isDraft' => $row['mt_is_draft'],
665+
'isDraft' => $row['mt_is_draft'] ?? 0,
663666
];
664667

665668
ImportHandler::getInstance()
@@ -920,7 +923,10 @@ public function exportThreads($offset, $limit)
920923
*/
921924
public function countPosts()
922925
{
923-
return $this->__getMaxID($this->databasePrefix . "forums_posts", 'pid');
926+
return \max(
927+
$this->__getMaxID($this->databasePrefix . "forums_posts", 'pid'),
928+
$this->__getMaxID($this->databasePrefix . "forums_archive_posts", 'archive_id')
929+
);
924930
}
925931

926932
/**
@@ -931,12 +937,22 @@ public function countPosts()
931937
*/
932938
public function exportPosts($offset, $limit)
933939
{
934-
$sql = "SELECT *
940+
$sql = "SELECT pid, topic_id, author_id, author_name,
941+
post, post_date, queued,
942+
edit_time, post_edit_reason,
943+
ip_address, pdelete_time
935944
FROM " . $this->databasePrefix . "forums_posts
936945
WHERE pid BETWEEN ? AND ?
946+
UNION
947+
SELECT archive_id AS pid, archive_topic_id AS topic_id, archive_author_id AS author_id, archive_author_name AS author_name,
948+
archive_content AS post, archive_content_date AS post_date, archive_queued AS queued,
949+
archive_edit_time AS edit_time, archive_edit_reason AS post_edit_reason,
950+
archive_ip_address AS ip_address, 0 AS pdelete_time
951+
FROM " . $this->databasePrefix . "forums_archive_posts
952+
WHERE archive_id BETWEEN ? AND ?
937953
ORDER BY pid";
938954
$statement = $this->database->prepareUnmanaged($sql);
939-
$statement->execute([$offset + 1, $offset + $limit]);
955+
$statement->execute([$offset + 1, $offset + $limit, $offset + 1, $offset + $limit]);
940956
while ($row = $statement->fetchArray()) {
941957
$data = [
942958
'threadID' => $row['topic_id'],
@@ -1204,10 +1220,12 @@ public function exportPostAttachments($offset, $limit)
12041220
*/
12051221
private function countAttachments($type)
12061222
{
1223+
$id = ($type === 'blog_Entries' ? 'id1' : 'id2');
1224+
12071225
$sql = "SELECT COUNT(*) AS count
12081226
FROM " . $this->databasePrefix . "core_attachments_map
12091227
WHERE location_key = ?
1210-
AND id2 IS NOT NULL";
1228+
AND {$id} IS NOT NULL";
12111229
$statement = $this->database->prepareUnmanaged($sql);
12121230
$statement->execute([$type]);
12131231
$row = $statement->fetchArray();
@@ -1225,12 +1243,14 @@ private function countAttachments($type)
12251243
*/
12261244
private function exportAttachments($type, $objectType, $offset, $limit)
12271245
{
1228-
$sql = "SELECT core_attachments.*, core_attachments_map.id2
1246+
$id = ($type === 'blog_Entries' ? 'id1' : 'id2');
1247+
1248+
$sql = "SELECT core_attachments.*, core_attachments_map.id2, core_attachments_map.id1
12291249
FROM " . $this->databasePrefix . "core_attachments_map core_attachments_map
12301250
LEFT JOIN " . $this->databasePrefix . "core_attachments core_attachments
12311251
ON core_attachments.attach_id = core_attachments_map.attachment_id
12321252
WHERE core_attachments_map.location_key = ?
1233-
AND core_attachments_map.id2 IS NOT NULL
1253+
AND core_attachments_map.{$id} IS NOT NULL
12341254
ORDER BY core_attachments_map.attachment_id";
12351255
$statement = $this->database->prepareUnmanaged($sql, $limit, $offset);
12361256
$statement->execute([$type]);
@@ -1242,7 +1262,7 @@ private function exportAttachments($type, $objectType, $offset, $limit)
12421262
$fileLocation = $this->fileSystemPath . 'uploads/' . $row['attach_location'];
12431263

12441264
$data = [
1245-
'objectID' => $row['id2'],
1265+
'objectID' => $row[$id],
12461266
'userID' => $row['attach_member_id'] ?: null,
12471267
'filename' => $row['attach_file'],
12481268
'downloads' => $row['attach_hits'],
@@ -1535,6 +1555,7 @@ public function exportBlogs($offset, $limit)
15351555
'title' => $this->getLanguageVar('blogs_blog', $row['blog_id']),
15361556
'description' => self::fixMessage($this->getLanguageVar('blogs_blog', $row['blog_id'], 'desc')),
15371557
'isFeatured' => $row['blog_pinned'],
1558+
'accessLevel' => isset($row['blog_social_group']) ? Blog::ACCESS_OWNER : Blog::ACCESS_EVERYONE,
15381559
];
15391560

15401561
$additionalData = [];
@@ -1558,7 +1579,7 @@ public function exportBlogs($offset, $limit)
15581579
public function countBlogCategories()
15591580
{
15601581
$sql = "SELECT COUNT(*) AS count
1561-
FROM " . $this->databasePrefix . "blog_entry_categories";
1582+
FROM " . $this->databasePrefix . "blog_categories";
15621583
$statement = $this->database->prepareUnmanaged($sql);
15631584
$statement->execute();
15641585
$row = $statement->fetchArray();
@@ -1575,18 +1596,18 @@ public function countBlogCategories()
15751596
public function exportBlogCategories($offset, $limit)
15761597
{
15771598
$sql = "SELECT *
1578-
FROM " . $this->databasePrefix . "blog_entry_categories
1579-
ORDER BY entry_category_id";
1599+
FROM " . $this->databasePrefix . "blog_categories
1600+
ORDER BY category_id";
15801601
$statement = $this->database->prepareUnmanaged($sql, $limit, $offset);
15811602
$statement->execute();
15821603
while ($row = $statement->fetchArray()) {
15831604
$data = [
1584-
'title' => $row['entry_category_name'],
1605+
'title' => $row['category_seo_name'],
15851606
];
15861607

15871608
ImportHandler::getInstance()
15881609
->getImporter('com.woltlab.blog.category')
1589-
->import($row['entry_category_id'], $data);
1610+
->import($row['category_id'], $data);
15901611
}
15911612
}
15921613

@@ -1629,8 +1650,9 @@ public function exportBlogEntries($offset, $limit)
16291650
$conditionBuilder = new PreparedStatementConditionBuilder();
16301651
$conditionBuilder->add('entry_id IN (?)', [$entryIDs]);
16311652

1632-
$sql = "SELECT *
1633-
FROM " . $this->databasePrefix . "blog_entries
1653+
$sql = "SELECT blog_entries.*,
1654+
(SELECT blog_category_id FROM " . $this->databasePrefix . "blog_blogs WHERE blog_id = blog_entries.entry_blog_id) AS category_id
1655+
FROM " . $this->databasePrefix . "blog_entries blog_entries
16341656
" . $conditionBuilder;
16351657
$statement = $this->database->prepareUnmanaged($sql);
16361658
$statement->execute($conditionBuilder->getParameters());
@@ -1639,8 +1661,11 @@ public function exportBlogEntries($offset, $limit)
16391661
if (isset($tags[$row['entry_id']])) {
16401662
$additionalData['tags'] = $tags[$row['entry_id']];
16411663
}
1642-
if ($row['entry_category_id']) {
1643-
$additionalData['categories'] = [$row['entry_category_id']];
1664+
if ($row['category_id']) {
1665+
$additionalData['categories'] = [$row['category_id']];
1666+
}
1667+
if (!empty($row['entry_cover_photo'])) {
1668+
$additionalData['coverPhoto'] = $this->fileSystemPath . 'uploads/' . $row['entry_cover_photo'];
16441669
}
16451670

16461671
$data = [
@@ -1835,7 +1860,7 @@ private function getDefaultLanguageID()
18351860
private function getLanguageVar($prefix, $id, $suffix = '')
18361861
{
18371862
if ($this->languageStatement === null) {
1838-
$sql = "SELECT word_custom
1863+
$sql = "SELECT word_custom, word_default
18391864
FROM " . $this->databasePrefix . "core_sys_lang_words
18401865
WHERE lang_id = ?
18411866
AND word_key = ?";
@@ -1847,7 +1872,7 @@ private function getLanguageVar($prefix, $id, $suffix = '')
18471872
]);
18481873
$row = $this->languageStatement->fetchArray();
18491874
if ($row !== false) {
1850-
return $row['word_custom'];
1875+
return $row['word_custom'] ?: $row['word_default'];
18511876
}
18521877

18531878
return '';
@@ -1884,6 +1909,9 @@ static function ($content) use (&$codes) {
18841909
$string
18851910
);
18861911

1912+
// Remove blank paragraphs, as Invision uses them to create spacing between other paragraphs.
1913+
$string = \str_ireplace('<p></p>', '', $string);
1914+
18871915
// <p> to newline
18881916
$string = \str_ireplace('<p>', "", $string);
18891917
$string = \str_ireplace('</p>', "\n\n", $string);
@@ -1961,6 +1989,12 @@ static function ($matches) {
19611989
$string
19621990
);
19631991

1992+
// replace base_url placeholder in urls/images
1993+
$string = \str_ireplace('<___base_url___>/', WCF::getPath(), $string);
1994+
1995+
// replace `<fileStore.core_Attachment>` to simplify regex
1996+
$string = \str_ireplace('<fileStore.core_Attachment>', '', $string);
1997+
19641998
// embedded attachments
19651999
$string = \preg_replace(
19662000
'~<a class="ipsAttachLink" (?:rel="[^"]*" )?href="[^"]*id=(\d+)[^"]*".*?</a>~i',
@@ -1972,6 +2006,11 @@ static function ($matches) {
19722006
'[attach]\\1[/attach]',
19732007
$string
19742008
);
2009+
$string = \preg_replace(
2010+
'~<img[^>]*data-fileid="(\d+)"[^>]*>~i',
2011+
'[attach]\\1[/attach]',
2012+
$string
2013+
);
19752014

19762015
// urls
19772016
$string = \preg_replace(
@@ -1991,14 +2030,21 @@ static function ($matches) {
19912030
"[quote='\\1']\\2[/quote]",
19922031
$string
19932032
);
2033+
$string = \preg_replace(
2034+
'~<blockquote[^>]*data-ipsquote-username="([^"]+)"[^>]*>(.*?)</blockquote>~is',
2035+
"[quote='\\1']\\2[/quote]",
2036+
$string
2037+
);
19942038
$string = \preg_replace(
19952039
'~<blockquote[^>]*>(.*?)</blockquote>~is',
19962040
'[quote]\\1[/quote]',
19972041
$string
19982042
);
1999-
2000-
// replace base_url placeholder in urls/images
2001-
$string = \str_ireplace('<___base_url___>/', WCF::getPath(), $string);
2043+
$string = \preg_replace(
2044+
'~<header class="ipsQuote_citation">(.*?)</header>~is',
2045+
'',
2046+
$string
2047+
);
20022048

20032049
// code
20042050
for ($i = 0, $length = \count($codes); $i < $length; $i++) {
@@ -2029,6 +2075,9 @@ static function ($matches) {
20292075
// images
20302076
$string = \preg_replace('~<img[^>]+src=["\']([^"\']+)["\'][^>]*/?>~is', '[img]\\1[/img]', $string);
20312077

2078+
// youtube
2079+
$string = \preg_replace('~<iframe[^>]*src="https://www.youtube(?:-nocookie)?.com/embed/([a-zA-Z0-9_-]+)[^"]*"[^>]*></iframe>~is', '[media]https://www.youtube.com/watch?v=\\1[/media]', $string);
2080+
20322081
// strip tags
20332082
$string = StringUtil::stripHTML($string);
20342083

files/lib/system/exporter/VB5xExporter.class.php

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,13 @@ public function validateFileAccess()
170170

171171
if (\in_array('com.woltlab.wcf.user.avatar', $this->selectedData)) {
172172
if ($this->readOption('usefileavatar')) {
173-
// TODO: Not yet supported
174-
return false;
173+
$path = $this->readOption('avatarpath');
174+
if (!\str_starts_with($path, '/')) {
175+
$path = \realpath($this->fileSystemPath . $path);
176+
}
177+
if (!\is_dir($path)) {
178+
return false;
179+
}
175180
}
176181
}
177182

@@ -483,13 +488,12 @@ public function exportUserAvatars($offset, $limit)
483488
$file = null;
484489

485490
try {
486-
// TODO: not yet supported
487-
if (false && $this->readOption('usefileavatar')) {
491+
if ($this->readOption('usefileavatar')) {
488492
$file = $this->readOption('avatarpath');
489493
if (!\str_starts_with($file, '/')) {
490494
$file = \realpath($this->fileSystemPath . $file);
491495
}
492-
$file = FileUtil::addTrailingSlash($file) . 'avatar' . $row['userid'] . '_' . $row['avatarrevision'] . '.gif';
496+
$file = FileUtil::addTrailingSlash($file) . $row['filename'];
493497
} else {
494498
$file = FileUtil::getTemporaryFilename('avatar_');
495499
\file_put_contents($file, $row['filedata']);
@@ -755,7 +759,7 @@ protected function exportBoardsRecursively($parentID = 0)
755759
'position' => $board['displayorder'] ?: 0,
756760
'boardType' => $boardType,
757761
'title' => $board['title'],
758-
'description' => $board['description'],
762+
'description' => $board['description'] ?? '',
759763
'descriptionUseHtml' => 0,
760764
'enableMarkingAsDone' => 0,
761765
'ignorable' => 1,
@@ -1340,7 +1344,7 @@ public function exportBlogAttachments($offset, $limit)
13401344
INNER JOIN (
13411345
SELECT contenttypeid
13421346
FROM " . $this->databasePrefix . "contenttype
1343-
WHERE class IN(?)
1347+
WHERE class IN (?)
13441348
) x
13451349
ON x.contenttypeid = grandparent.contenttypeid
13461350
INNER JOIN (
@@ -1369,6 +1373,23 @@ public function exportBlogAttachments($offset, $limit)
13691373
$file = FileUtil::getTemporaryFilename('attachment_');
13701374
\file_put_contents($file, $row['filedata']);
13711375
break;
1376+
1377+
case self::ATTACHFILE_FILESYSTEM:
1378+
$file = $this->readOption('attachpath');
1379+
if (!StringUtil::startsWith($file, '/')) {
1380+
$file = \realpath($this->fileSystemPath . $file);
1381+
}
1382+
$file = FileUtil::addTrailingSlash($file);
1383+
$file .= $row['userid'] . '/' . $row['filedataid'] . '.attach';
1384+
break;
1385+
case self::ATTACHFILE_FILESYSTEM_SUBFOLDER:
1386+
$file = $this->readOption('attachpath');
1387+
if (!StringUtil::startsWith($file, '/')) {
1388+
$file = \realpath($this->fileSystemPath . $file);
1389+
}
1390+
$file = FileUtil::addTrailingSlash($file);
1391+
$file .= \implode('/', \str_split($row['userid'])) . '/' . $row['filedataid'] . '.attach';
1392+
break;
13721393
}
13731394

13741395
// unable to read file -> abort
@@ -1552,6 +1573,23 @@ public function exportGalleryImages($offset, $limit)
15521573
$file = FileUtil::getTemporaryFilename('attachment_');
15531574
\file_put_contents($file, $row['filedata']);
15541575
break;
1576+
1577+
case self::ATTACHFILE_FILESYSTEM:
1578+
$file = $this->readOption('attachpath');
1579+
if (!StringUtil::startsWith($file, '/')) {
1580+
$file = \realpath($this->fileSystemPath . $file);
1581+
}
1582+
$file = FileUtil::addTrailingSlash($file);
1583+
$file .= $row['userid'] . '/' . $row['filedataid'] . '.attach';
1584+
break;
1585+
case self::ATTACHFILE_FILESYSTEM_SUBFOLDER:
1586+
$file = $this->readOption('attachpath');
1587+
if (!StringUtil::startsWith($file, '/')) {
1588+
$file = \realpath($this->fileSystemPath . $file);
1589+
}
1590+
$file = FileUtil::addTrailingSlash($file);
1591+
$file .= \implode('/', \str_split($row['userid'])) . '/' . $row['filedataid'] . '.attach';
1592+
break;
15551593
}
15561594

15571595
// unable to read file -> abort

0 commit comments

Comments
 (0)