Skip to content

Commit 01a2c45

Browse files
authored
Merge pull request #6188 from WoltLab/6.2-attachment-list-to-grid-view
Migrate `AttachmentListPage` to grid view
2 parents 606c83a + bd9f5b7 commit 01a2c45

File tree

15 files changed

+493
-327
lines changed

15 files changed

+493
-327
lines changed
Lines changed: 4 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
11
{include file='header' pageTitle='wcf.acp.attachment.list'}
22

33
{include file='shared_imageViewer'}
4-
<script data-relocate="true">
5-
require(['WoltLabSuite/Core/Ui/User/Search/Input'], (UiUserSearchInput) => {
6-
new UiUserSearchInput(document.getElementById('username'));
7-
});
8-
</script>
94

105
<header class="contentHeader">
116
<div class="contentHeaderTitle">
12-
<h1 class="contentTitle">{lang}wcf.acp.attachment.list{/lang}{if $items} <span class="badge badgeInverse">{#$items}</span>{/if}</h1>
7+
<h1 class="contentTitle">{lang}wcf.acp.attachment.list{/lang}</h1>
138
<p class="contentHeaderDescription">{lang}wcf.acp.attachment.stats{/lang}</p>
149
</div>
1510

@@ -22,140 +17,8 @@
2217
{/hascontent}
2318
</header>
2419

25-
{include file='shared_formError'}
26-
27-
<form method="post" action="{link controller='AttachmentList'}{/link}">
28-
<section class="section">
29-
<h2 class="sectionTitle">{lang}wcf.global.filter{/lang}</h2>
30-
31-
<div class="row rowColGap formGrid">
32-
<dl class="col-xs-12 col-md-3">
33-
<dt></dt>
34-
<dd>
35-
<input type="number" id="attachmentID" name="attachmentID" value="{if $attachmentID}{$attachmentID}{/if}" placeholder="{lang}wcf.global.objectID{/lang}" class="long">
36-
</dd>
37-
</dl>
38-
39-
<dl class="col-xs-12 col-md-3">
40-
<dt></dt>
41-
<dd>
42-
<input type="text" id="username" name="username" value="{$username}" placeholder="{lang}wcf.user.username{/lang}" class="long">
43-
</dd>
44-
</dl>
45-
46-
<dl class="col-xs-12 col-md-3">
47-
<dt></dt>
48-
<dd>
49-
<input type="text" id="filename" name="filename" value="{$filename}" placeholder="{lang}wcf.attachment.filename{/lang}" class="long">
50-
</dd>
51-
</dl>
52-
53-
{if $availableFileTypes|count > 1}
54-
<dl class="col-xs-12 col-md-3">
55-
<dt></dt>
56-
<dd>
57-
<select name="fileType" id="fileType">
58-
<option value="">{lang}wcf.attachment.fileType{/lang}</option>
59-
{htmlOptions options=$availableFileTypes selected=$fileType}
60-
</select>
61-
</dd>
62-
</dl>
63-
{/if}
64-
65-
{event name='filterFields'}
66-
</div>
67-
68-
<div class="formSubmit">
69-
<input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s">
70-
{csrfToken}
71-
</div>
72-
</section>
73-
</form>
74-
75-
{hascontent}
76-
<div class="paginationTop">
77-
{content}
78-
{assign var='linkParameters' value=''}
79-
{if $username}{capture append=linkParameters}&username={@$username|rawurlencode}{/capture}{/if}
80-
{if $filename}{capture append=linkParameters}&filename={@$filename|rawurlencode}{/capture}{/if}
81-
{if $fileType}{capture append=linkParameters}&fileType={@$fileType|rawurlencode}{/capture}{/if}
82-
83-
{pages print=true assign=pagesLinks controller="AttachmentList" link="pageNo=%d&sortField=$sortField&sortOrder=$sortOrder$linkParameters"}
84-
{/content}
85-
</div>
86-
{/hascontent}
87-
88-
{if $objects|count}
89-
<div class="section tabularBox">
90-
<table class="table jsObjectActionContainer" data-object-action-class-name="wcf\data\attachment\AttachmentAction"
91-
<thead>
92-
<tr>
93-
<th class="columnID columnAttachmentID{if $sortField == 'attachmentID'} active {@$sortOrder}{/if}" colspan="2"><a href="{link controller='AttachmentList'}pageNo={@$pageNo}&sortField=attachmentID&sortOrder={if $sortField == 'attachmentID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$linkParameters}{/link}">{lang}wcf.global.objectID{/lang}</a></th>
94-
<th class="columnTitle columnFilename{if $sortField == 'filename'} active {@$sortOrder}{/if}"><a href="{link controller='AttachmentList'}pageNo={@$pageNo}&sortField=filename&sortOrder={if $sortField == 'filename' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$linkParameters}{/link}">{lang}wcf.attachment.filename{/lang}</a></th>
95-
<th class="columnDate columnUploadTime{if $sortField == 'uploadTime'} active {@$sortOrder}{/if}"><a href="{link controller='AttachmentList'}pageNo={@$pageNo}&sortField=uploadTime&sortOrder={if $sortField == 'uploadTime' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$linkParameters}{/link}">{lang}wcf.attachment.uploadTime{/lang}</a></th>
96-
<th class="columnDigits columnFilesize{if $sortField == 'filesize'} active {@$sortOrder}{/if}"><a href="{link controller='AttachmentList'}pageNo={@$pageNo}&sortField=filesize&sortOrder={if $sortField == 'filesize' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$linkParameters}{/link}">{lang}wcf.attachment.filesize{/lang}</a></th>
97-
<th class="columnDigits columnDownloads{if $sortField == 'downloads'} active {@$sortOrder}{/if}"><a href="{link controller='AttachmentList'}pageNo={@$pageNo}&sortField=downloads&sortOrder={if $sortField == 'downloads' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$linkParameters}{/link}">{lang}wcf.attachment.downloads{/lang}</a></th>
98-
<th class="columnDate columnLastDownloadTime{if $sortField == 'lastDownloadTime'} active {@$sortOrder}{/if}"><a href="{link controller='AttachmentList'}pageNo={@$pageNo}&sortField=lastDownloadTime&sortOrder={if $sortField == 'lastDownloadTime' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$linkParameters}{/link}">{lang}wcf.attachment.lastDownloadTime{/lang}</a></th>
99-
100-
{event name='columnHeads'}
101-
</tr>
102-
</thead>
103-
104-
<tbody class="jsReloadPageWhenEmpty">
105-
{foreach from=$objects item=attachment}
106-
<tr class="jsAttachmentRow jsObjectActionObject" data-object-id="{@$attachment->getObjectID()}">
107-
<td class="columnIcon">
108-
{objectAction action="delete" objectTitle=$attachment->filename|tableWordwrap}
109-
110-
{event name='rowButtons'}
111-
</td>
112-
<td class="columnID columnAttachmentID">{@$attachment->attachmentID}</td>
113-
<td class="columnTitle columnFilename">
114-
<div class="box64">
115-
<a href="{$attachment->getLink()}"{if $attachment->isImage} data-fancybox="attachments" data-caption="{$attachment->filename}"{/if}>
116-
{if $attachment->tinyThumbnailType}
117-
<img src="{$attachment->getThumbnailLink('tiny')}" class="attachmentTinyThumbnail" alt="">
118-
{else}
119-
{icon size=64 name=$attachment->getIconName()}
120-
{/if}
121-
</a>
122-
123-
<div>
124-
<p><a href="{$attachment->getLink()}">{$attachment->filename|tableWordwrap}</a></p>
125-
<p><small>{if $attachment->userID}{if $__wcf->session->getPermission('admin.user.canEditUser')}<a href="{link controller='UserEdit' id=$attachment->userID}{/link}">{$attachment->username}</a>{else}{$attachment->username}{/if}{else}{lang}wcf.user.guest{/lang}{/if}</small></p>
126-
{if $attachment->getContainerObject()}<p><small><a href="{$attachment->getContainerObject()->getLink()}">{$attachment->getContainerObject()->getTitle()|tableWordwrap}</a></small></p>{/if}
127-
</div>
128-
</div>
129-
</td>
130-
<td class="columnDate columnUploadTime">{@$attachment->uploadTime|time}</td>
131-
<td class="columnDigits columnFilesize">{@$attachment->filesize|filesize}</td>
132-
<td class="columnDigits columnDownloads">{if $attachment->downloads}{#$attachment->downloads}{/if}</td>
133-
<td class="columnDate columnLastDownloadTime">{if $attachment->lastDownloadTime}{@$attachment->lastDownloadTime|time}{/if}</td>
134-
135-
{event name='columns'}
136-
</tr>
137-
{/foreach}
138-
</tbody>
139-
</table>
140-
</div>
141-
142-
<footer class="contentFooter">
143-
{hascontent}
144-
<div class="paginationBottom">
145-
{content}{@$pagesLinks}{/content}
146-
</div>
147-
{/hascontent}
148-
149-
{hascontent}
150-
<nav class="contentFooterNavigation">
151-
<ul>
152-
{content}{event name='contentFooterNavigation'}{/content}
153-
</ul>
154-
</nav>
155-
{/hascontent}
156-
</footer>
157-
{else}
158-
<woltlab-core-notice type="info">{lang}wcf.global.noItems{/lang}</woltlab-core-notice>
159-
{/if}
20+
<div class="section">
21+
{unsafe:$gridView->render()}
22+
</div>
16023

16124
{include file='footer'}

wcfsetup/install/files/lib/acp/page/AttachmentListPage.class.php

Lines changed: 26 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,20 @@
22

33
namespace wcf\acp\page;
44

5-
use wcf\data\attachment\AdministrativeAttachmentList;
6-
use wcf\data\object\type\ObjectTypeCache;
7-
use wcf\data\user\User;
8-
use wcf\page\SortablePage;
5+
use wcf\page\AbstractGridViewPage;
6+
use wcf\system\gridView\admin\AttachmentGridView;
97
use wcf\system\WCF;
10-
use wcf\util\StringUtil;
118

129
/**
1310
* Shows a list of attachments.
1411
*
15-
* @author Marcel Werk
16-
* @copyright 2001-2019 WoltLab GmbH
17-
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
12+
* @author Olaf Braun, Marcel Werk
13+
* @copyright 2001-2025 WoltLab GmbH
14+
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
1815
*
19-
* @property AdministrativeAttachmentList $objectList
16+
* @property AttachmentGridView $gridView
2017
*/
21-
class AttachmentListPage extends SortablePage
18+
class AttachmentListPage extends AbstractGridViewPage
2219
{
2320
/**
2421
* @inheritDoc
@@ -30,139 +27,33 @@ class AttachmentListPage extends SortablePage
3027
*/
3128
public $neededPermissions = ['admin.attachment.canManageAttachment'];
3229

33-
/**
34-
* @inheritDoc
35-
*/
36-
public $defaultSortField = 'uploadTime';
37-
38-
/**
39-
* @inheritDoc
40-
*/
41-
public $defaultSortOrder = 'DESC';
42-
43-
/**
44-
* @inheritDoc
45-
*/
46-
public $validSortFields = ['attachmentID', 'filename', 'filesize', 'downloads', 'uploadTime', 'lastDownloadTime'];
47-
48-
/**
49-
* @inheritDoc
50-
*/
51-
public $objectListClassName = AdministrativeAttachmentList::class;
52-
53-
/**
54-
* username
55-
* @var string
56-
*/
57-
public $username = '';
58-
59-
/**
60-
* filename
61-
* @var string
62-
*/
63-
public $filename = '';
64-
65-
/**
66-
* file type
67-
* @var string
68-
*/
69-
public $fileType = '';
70-
71-
/**
72-
* available file types
73-
* @var string[]
74-
*/
75-
public $availableFileTypes = [];
76-
77-
/**
78-
* @var int
79-
*/
80-
public $attachmentID = 0;
81-
82-
/**
83-
* attachment stats
84-
* @var array
85-
*/
86-
public $stats = [];
87-
88-
/**
89-
* @inheritDoc
90-
*/
91-
public function readParameters()
30+
#[\Override]
31+
protected function createGridViewController(): AttachmentGridView
9232
{
93-
parent::readParameters();
94-
95-
if (!empty($_REQUEST['username'])) {
96-
$this->username = StringUtil::trim($_REQUEST['username']);
97-
}
98-
if (!empty($_REQUEST['filename'])) {
99-
$this->filename = StringUtil::trim($_REQUEST['filename']);
100-
}
101-
if (!empty($_REQUEST['fileType'])) {
102-
$this->fileType = $_REQUEST['fileType'];
103-
}
104-
if (!empty($_REQUEST['attachmentID'])) {
105-
$this->attachmentID = \intval($_REQUEST['attachmentID']);
106-
}
33+
return new AttachmentGridView();
10734
}
10835

109-
/**
110-
* @inheritDoc
111-
*/
112-
protected function initObjectList()
113-
{
114-
parent::initObjectList();
115-
116-
$objectTypeIDs = [];
117-
foreach (ObjectTypeCache::getInstance()->getObjectTypes('com.woltlab.wcf.attachment.objectType') as $objectType) {
118-
if (!$objectType->private) {
119-
$objectTypeIDs[] = $objectType->objectTypeID;
120-
}
121-
}
122-
123-
if (!empty($objectTypeIDs)) {
124-
$this->objectList->getConditionBuilder()->add('attachment.objectTypeID IN (?)', [$objectTypeIDs]);
125-
} else {
126-
$this->objectList->getConditionBuilder()->add('1 = 0');
127-
}
128-
$this->objectList->getConditionBuilder()->add("attachment.tmpHash = ''");
129-
130-
// get data
131-
$this->stats = $this->objectList->getStats();
132-
$this->availableFileTypes = $this->objectList->getAvailableFileTypes();
133-
134-
// filter
135-
if (!empty($this->username)) {
136-
$user = User::getUserByUsername($this->username);
137-
if ($user->userID) {
138-
$this->objectList->getConditionBuilder()->add('attachment.userID = ?', [$user->userID]);
139-
}
140-
}
141-
if (!empty($this->filename)) {
142-
$this->objectList->getConditionBuilder()->add('attachment.filename LIKE ?', [$this->filename . '%']);
143-
}
144-
if (!empty($this->fileType)) {
145-
$this->objectList->getConditionBuilder()->add('attachment.fileType LIKE ?', [$this->fileType]);
146-
}
147-
if ($this->attachmentID) {
148-
$this->objectList->getConditionBuilder()->add('attachment.attachmentID = ?', [$this->attachmentID]);
149-
}
150-
}
151-
152-
/**
153-
* @inheritDoc
154-
*/
36+
#[\Override]
15537
public function assignVariables()
15638
{
15739
parent::assignVariables();
15840

15941
WCF::getTPL()->assign([
160-
'stats' => $this->stats,
161-
'username' => $this->username,
162-
'filename' => $this->filename,
163-
'fileType' => $this->fileType,
164-
'availableFileTypes' => $this->availableFileTypes,
165-
'attachmentID' => $this->attachmentID,
42+
'stats' => $this->getAttachmentStats()
16643
]);
16744
}
45+
46+
private function getAttachmentStats(): array
47+
{
48+
$sql = "SELECT COUNT(*) AS count,
49+
COALESCE(SUM(file.fileSize), 0) AS size,
50+
COALESCE(SUM(downloads), 0) AS downloads
51+
FROM wcf1_attachment attachment
52+
LEFT JOIN wcf1_file file
53+
ON (file.fileID = attachment.fileID)";
54+
$statement = WCF::getDB()->prepare($sql);
55+
$statement->execute();
56+
57+
return $statement->fetchArray();
58+
}
16859
}

wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ static function (\wcf\event\endpoint\ControllerCollecting $event) {
161161
$event->register(new \wcf\system\endpoint\controller\core\articles\RestoreArticle());
162162
$event->register(new \wcf\system\endpoint\controller\core\articles\PublishArticle());
163163
$event->register(new \wcf\system\endpoint\controller\core\articles\UnpublishArticle());
164+
$event->register(new \wcf\system\endpoint\controller\core\attachments\DeleteAttachment());
164165
}
165166
);
166167

0 commit comments

Comments
 (0)