<?xml version="1.0"?>
<rss version="2.0"><channel><title>Articles: Forum Software</title><link>https://lalu.pro/articles/forum-software/?d=1</link><description>Articles: Forum Software</description><language>en</language><item><title>Tutorial: Membuat Cover Forum Style IPS untuk XenForo</title><link>https://lalu.pro/articles/forum-software/tutorial-membuat-cover-forum-style-ips-untuk-xenforo-r4/</link><description><![CDATA[<p>Panduan lengkap membangun tampilan <strong>cover forum</strong> dengan layout 2 kolom (gambar + info di kiri, daftar thread terbaru di kanan) — mirip gaya Invision Community / IPS, tapi natif di XenForo. Tanpa beli style komersial.</p><p><strong>Estimasi waktu:</strong> 30–45 menit <strong>Tingkat:</strong> Menengah (perlu nyaman edit file PHP via FTP dan template XenForo)</p><p>Konsep tampilan akhir: cover di kiri, daftar thread terbaru di kanan, border atas warna berbeda per kategori.</p><p>Lihat demo: <a rel="external nofollow" href="https://forum.intutekno.com/">https://forum.intutekno.com/</a></p><hr><h2>1. Yang Akan Dibuat</h2><p>Setiap forum di halaman utama akan menampilkan:</p><p><strong>Kolom kiri (30% lebar):</strong></p><ul><li><p>Cover image 3:2 (SVG/WebP/JPG)</p></li><li><p>Judul forum (link ke forum)</p></li><li><p>Deskripsi forum</p></li><li><p>Statistik (jumlah threads &amp; messages)</p></li></ul><p><strong>Kolom kanan (70% lebar):</strong></p><ul><li><p>5 thread terbaru dengan avatar, judul, username, tanggal, jumlah replies</p></li><li><p>Atau ikon + pesan "Belum ada thread" kalau forum masih kosong</p></li></ul><p><strong>Bonus:</strong> Border-top tiap card forum bisa diwarnai berbeda berdasarkan kategori induk-nya.</p><hr><h2>2. Prasyarat</h2><ul><li><p>XenForo 2.3.x (panduan ini diuji di 2.3.10)</p></li><li><p>Akses <strong>AdminCP</strong></p></li><li><p>Akses <strong>FTP/File Manager</strong> ke server (untuk upload PHP files)</p></li><li><p>Akses <strong>cPanel</strong> atau setara untuk backup database (opsional tapi disarankan)</p></li><li><p>Pemahaman dasar HTML/CSS akan membantu</p></li></ul><hr><h2>3. Backup Dulu</h2><p>Sebelum apa pun, lakukan dua backup:</p><p><strong>Backup database:</strong></p><ul><li><p>cPanel → phpMyAdmin → pilih database forum → Export → Quick → Go</p></li></ul><p><strong>Backup file:</strong></p><ul><li><p>File Manager cPanel → kompres folder forum jadi <code>.zip</code> → download</p></li></ul><p>Kalau ada yang salah saat ngoprek, kita bisa balik ke kondisi awal.</p><hr><h2>4. Aktifkan Development Mode</h2><p>Buka <code>/src/config.php</code> lewat File Manager atau FTP. Tambahkan dua baris ini di paling bawah:</p><pre spellcheck="" class="ipsCode language-php" data-language="PHP"><code>$config['development']['enabled'] = true;
$config['development']['defaultAddOn'] = 'Intutekno/CoverForum';
</code></pre><p>Ganti <code>Intutekno/CoverForum</code> sesuai nama yang Anda inginkan (format: <code>VendorName/AddOnName</code>). Setelah save, refresh AdminCP — menu <strong>Development</strong> dan <strong>Add-ons</strong> akan muncul di sidebar.</p><hr><h2>5. Bikin Add-on PHP</h2><p>Add-on ini bertugas menjalankan satu fungsi: mengambil 5 thread terbaru dari sebuah forum dan menyediakannya untuk dipakai di template.</p><h3>5.1. Buat folder add-on</h3><p>Via FTP, buat struktur folder:</p><pre spellcheck="" class="ipsCode language-plaintext" data-language="Plain Text"><code>/src/addons/Intutekno/CoverForum/
</code></pre><h3>5.2. Buat file <code>addon.json</code></h3><p>Isi:</p><pre spellcheck="" class="ipsCode language-json" data-language="JSON"><code>{
    "title": "Intutekno Cover Forum",
    "version_string": "1.0.0",
    "version_id": 1000070,
    "dev": "Intutekno",
    "supported": true
}
</code></pre><h3>5.3. Buat file <code>Listener.php</code></h3><pre spellcheck="" class="ipsCode language-php" data-language="PHP"><code>&lt;?php
namespace Intutekno\CoverForum;

class Listener
{
    public static function templaterSetup(\XF\Container $container, \XF\Template\Templater &amp;$templater)
    {
        $templater-&gt;addFunction('latest_forum_threads',
            ['Intutekno\CoverForum\TemplateHelper', 'templaterFunction']
        );
    }
}
</code></pre><h3>5.4. Buat file <code>TemplateHelper.php</code></h3><pre spellcheck="" class="ipsCode language-php" data-language="PHP"><code>&lt;?php
namespace Intutekno\CoverForum;

class TemplateHelper
{
    public static function templaterFunction($templater, &amp;$escape, $nodeId, $limit = 5)
    {
        $escape = false;
        return self::getLatestThreads($nodeId, $limit);
    }

    public static function getLatestThreads($nodeId, $limit = 5)
    {
        $finder = \XF::finder('XF:Thread')
            -&gt;where('node_id', $nodeId)
            -&gt;where('discussion_state', 'visible')
            -&gt;where('discussion_type', '&lt;&gt;', 'redirect')
            -&gt;with('User')
            -&gt;with('Prefix')
            -&gt;order('last_post_date', 'DESC')
            -&gt;limit($limit);

        return $finder-&gt;fetch();
    }
}
</code></pre><p><strong>Struktur akhir folder:</strong></p><pre spellcheck="" class="ipsCode language-plaintext" data-language="Plain Text"><code>/src/addons/Intutekno/CoverForum/
├── addon.json
├── Listener.php
└── TemplateHelper.php
</code></pre><hr><h2>6. Install Add-on</h2><p>Di AdminCP:</p><ol><li><p>Klik menu <strong>Add-ons</strong> di sidebar</p></li><li><p>Cari <strong>"Intutekno Cover Forum"</strong> di list</p></li><li><p>Klik tombol <strong>Install</strong> di sampingnya</p></li><li><p>Confirm</p></li></ol><p>Status add-on harus jadi <strong>Active</strong> (toggle hijau).</p><hr><h2>7. Register Code Event Listener</h2><p>Ini langkah yang menghubungkan add-on PHP kita ke template engine XenForo.</p><ol><li><p>AdminCP → <strong>Development</strong> → <strong>Code event listeners</strong></p></li><li><p>Klik <strong>+ Add code event listener</strong> di pojok kanan atas</p></li><li><p>Isi form:</p></li></ol><div class="ipsRichText__table-wrapper"><table style="width: 641px;"><colgroup><col style="width:223px;"><col style="width:418px;"></colgroup><tbody><tr><th colspan="1" rowspan="1"><p>Field</p></th><th colspan="1" rowspan="1"><p>Isi</p></th></tr><tr><td colspan="1" rowspan="1"><p><strong>Listen to event</strong></p></td><td colspan="1" rowspan="1"><p><code>templater_setup</code></p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>Event hint</strong></p></td><td colspan="1" rowspan="1"><p><em>(kosongkan)</em></p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>Execute callback: Class</strong></p></td><td colspan="1" rowspan="1"><p><code>Intutekno\CoverForum\Listener</code></p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>Execute callback: Method</strong></p></td><td colspan="1" rowspan="1"><p><code>templaterSetup</code></p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>Callback execution order</strong></p></td><td colspan="1" rowspan="1"><p><code>10</code> (default)</p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>Enable callback execution</strong></p></td><td colspan="1" rowspan="1"><p><span class="ipsEmoji" title="">✅</span> centang</p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>Description</strong></p></td><td colspan="1" rowspan="1"><p><code>Register latest_forum_threads function</code></p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>Add-on</strong></p></td><td colspan="1" rowspan="1"><p><code>Intutekno Cover Forum 1.0.0</code></p></td></tr></tbody></table></div><ol start="4"><li><p>Klik <strong>Save</strong></p></li></ol><hr><h2>8. Edit Template <code>node_list_forum</code></h2><p>AdminCP → <strong>Appearance → Templates</strong> → cari template <code>node_list_forum</code> → <strong>Edit</strong>.</p><p><strong>Ganti seluruh isi template</strong> dengan kode berikut:</p><pre spellcheck="" class="ipsCode language-plaintext" data-language="Plain Text"><code>&lt;xf:macro id="depth1" arg-node="!" arg-extras="!" arg-children="!" arg-childExtras="!" arg-depth="1"&gt;
	&lt;div class="block"&gt;
		&lt;div class="block-container"&gt;
			&lt;div class="block-body"&gt;
				&lt;xf:macro id="forum"
					arg-node="{$node}"
					arg-extras="{$extras}"
					arg-children="{$children}"
					arg-childExtras="{$childExtras}"
					arg-depth="{$depth}" /&gt;
			&lt;/div&gt;
		&lt;/div&gt;
	&lt;/div&gt;
&lt;/xf:macro&gt;

&lt;xf:macro id="depth2" arg-node="!" arg-extras="!" arg-children="!" arg-childExtras="!" arg-depth="1"&gt;
	&lt;xf:macro id="forum"
		arg-node="{$node}"
		arg-extras="{$extras}"
		arg-children="{$children}"
		arg-childExtras="{$childExtras}"
		arg-depth="{$depth}" /&gt;
&lt;/xf:macro&gt;

&lt;xf:macro id="depthN" arg-node="!" arg-extras="!" arg-children="!" arg-childExtras="!" arg-depth="1"&gt;
	&lt;li&gt;
		&lt;a href="{{ link('forums', $node) }}" class="subNodeLink subNodeLink--forum {{ $extras.hasNew ? 'subNodeLink--unread' : '' }}"&gt;
			&lt;xf:fa icon="{{ $node.Data.TypeHandler.getTypeIconClass() ?: 'fa-comments' }}" class="subNodeLink-icon" /&gt;{$node.title}
		&lt;/a&gt;
		&lt;xf:macro id="forum_list::sub_node_list"
			arg-children="{$children}"
			arg-childExtras="{$childExtras}"
			arg-depth="{{ $depth + 1 }}" /&gt;
	&lt;/li&gt;
&lt;/xf:macro&gt;

&lt;xf:macro id="forum"
	arg-node="!"
	arg-extras="!"
	arg-children="!"
	arg-childExtras="!"
	arg-depth="!"
	arg-chooseName=""
	arg-bonusInfo=""&gt;

	&lt;xf:if is="$depth == 1 OR $depth == 2"&gt;

		&lt;!-- ===== COVER STYLE (forum utama) ===== --&gt;
		&lt;div class="node node--id{$node.node_id} node--depth{$depth} node--forum intuteknoCover intuteknoCover--cat{$node.parent_node_id} {{ $extras.hasNew ? 'node--unread' : 'node--read' }}"&gt;
			&lt;div class="intuteknoCover-body"&gt;

				&lt;div class="intuteknoCover-side"&gt;
					&lt;a href="{{ link('forums', $node) }}" class="intuteknoCover-img"&gt;
						&lt;img src="/data/forum_covers/{$node.node_id}.svg"
							 onerror="this.src='/data/forum_covers/default.svg'"
							 alt="{$node.title}" loading="lazy" /&gt;
					&lt;/a&gt;
					&lt;h3 class="intuteknoCover-title"&gt;
						&lt;a href="{{ link('forums', $node) }}"&gt;{$node.title}&lt;/a&gt;
					&lt;/h3&gt;
					&lt;xf:if is="$node.description"&gt;
						&lt;div class="intuteknoCover-desc"&gt;{$node.description|raw}&lt;/div&gt;
					&lt;/xf:if&gt;
					&lt;div class="intuteknoCover-stats"&gt;
						&lt;span&gt;&lt;b&gt;{{ phrase('threads') }}:&lt;/b&gt; {$extras.discussion_count|number_short(1)}&lt;/span&gt;
						&lt;span&gt;&lt;b&gt;{{ phrase('messages') }}:&lt;/b&gt; {$extras.message_count|number_short(1)}&lt;/span&gt;
					&lt;/div&gt;
				&lt;/div&gt;

				&lt;div class="intuteknoCover-threads"&gt;
					&lt;xf:set var="$latest" value="{{ latest_forum_threads($node.node_id, 5) }}" /&gt;
					&lt;xf:if is="$latest is not empty"&gt;
						&lt;ul class="intuteknoThread-list"&gt;
							&lt;xf:foreach loop="$latest" value="$thread"&gt;
								&lt;li class="intuteknoThread-item"&gt;
									&lt;xf:avatar user="{$thread.User}" size="xxs" defaultname="{$thread.username}" class="intuteknoThread-avatar" /&gt;
									&lt;div class="intuteknoThread-main"&gt;
										&lt;div class="intuteknoThread-title"&gt;
											&lt;xf:if is="$thread.prefix_id"&gt;{{ prefix('thread', $thread) }}&lt;/xf:if&gt;
											&lt;a href="{{ link('threads', $thread) }}"&gt;{$thread.title}&lt;/a&gt;
										&lt;/div&gt;
										&lt;div class="intuteknoThread-meta"&gt;
											&lt;a href="{{ link('members', $thread.User) }}" class="username"&gt;{$thread.username}&lt;/a&gt;
											&lt;span&gt;·&lt;/span&gt;
											&lt;xf:date time="{$thread.last_post_date}" /&gt;
											&lt;span&gt;·&lt;/span&gt;
											&lt;span&gt;&lt;xf:fa icon="fa-comment-alt" /&gt; {$thread.reply_count|number_short(1)}&lt;/span&gt;
										&lt;/div&gt;
									&lt;/div&gt;
								&lt;/li&gt;
							&lt;/xf:foreach&gt;
						&lt;/ul&gt;
					&lt;xf:else /&gt;
						&lt;div class="intuteknoThread-empty"&gt;
							&lt;xf:fa icon="fa-comments" /&gt;
							&lt;span&gt;Belum ada thread di forum ini&lt;/span&gt;
						&lt;/div&gt;
					&lt;/xf:if&gt;
				&lt;/div&gt;

			&lt;/div&gt;
		&lt;/div&gt;

	&lt;xf:else /&gt;

		&lt;!-- ===== ORIGINAL STYLE (sub-forum, depth &gt;= 3) ===== --&gt;
		&lt;div class="node node--id{$node.node_id} node--depth{$depth} node--forum {{ $extras.hasNew ? 'node--unread' : 'node--read' }}"&gt;
			&lt;div class="node-body"&gt;
				&lt;span class="node-icon" aria-hidden="true"&gt;
					&lt;xf:fa icon="{{ $node.Data.TypeHandler.getTypeIconClass() ?: 'fa-comments' }}" /&gt;
				&lt;/span&gt;
				&lt;div class="node-main js-nodeMain"&gt;
					&lt;xf:if is="$chooseName"&gt;
						&lt;xf:checkbox standalone="true"&gt;
							&lt;xf:option labelclass="u-pullRight" class="js-chooseItem" name="{$chooseName}[]" value="{$node.node_id}" /&gt;
						&lt;/xf:checkbox&gt;
					&lt;/xf:if&gt;

					&lt;xf:set var="$descriptionDisplay" value="{{ property('nodeListDescriptionDisplay') }}" /&gt;
					&lt;h3 class="node-title"&gt;
						&lt;a href="{{ link('forums', $node) }}" data-xf-init="{{ $descriptionDisplay == 'tooltip' ? 'element-tooltip' : '' }}" data-shortcut="node-description"&gt;{$node.title}&lt;/a&gt;
					&lt;/h3&gt;
					&lt;xf:if is="$descriptionDisplay != 'none' &amp;&amp; $node.description"&gt;
						&lt;div class="node-description {{ $descriptionDisplay == 'tooltip' ? 'node-description--tooltip js-nodeDescTooltip' : '' }}"&gt;{$node.description|raw}&lt;/div&gt;
					&lt;/xf:if&gt;

					&lt;div class="node-meta"&gt;
						&lt;xf:if is="!{$extras.privateInfo}"&gt;
							&lt;div class="node-statsMeta"&gt;
								&lt;dl class="pairs pairs--inline"&gt;
									&lt;dt&gt;{{ phrase('threads') }}&lt;/dt&gt;
									&lt;dd&gt;{$extras.discussion_count|number_short(1)}&lt;/dd&gt;
								&lt;/dl&gt;
								&lt;dl class="pairs pairs--inline"&gt;
									&lt;dt&gt;{{ phrase('messages') }}&lt;/dt&gt;
									&lt;dd&gt;{$extras.message_count|number_short(1)}&lt;/dd&gt;
								&lt;/dl&gt;
							&lt;/div&gt;
						&lt;/xf:if&gt;

						&lt;xf:if is="$depth == 2 AND property('nodeListSubDisplay') == 'menu'"&gt;
							&lt;xf:macro id="forum_list::sub_nodes_menu"
								arg-children="{$children}"
								arg-childExtras="{$childExtras}"
								arg-depth="{{ $depth + 1 }}" /&gt;
						&lt;/xf:if&gt;
					&lt;/div&gt;

					&lt;xf:if is="$depth == 2 AND property('nodeListSubDisplay') == 'flat'"&gt;
						&lt;xf:macro id="forum_list::sub_nodes_flat"
							arg-children="{$children}"
							arg-childExtras="{$childExtras}"
							arg-depth="{{ $depth + 1 }}" /&gt;
					&lt;/xf:if&gt;

					&lt;xf:if is="$bonusInfo is not empty"&gt;
						&lt;div class="node-bonus"&gt;{$bonusInfo}&lt;/div&gt;
					&lt;/xf:if&gt;
				&lt;/div&gt;

				&lt;xf:if is="!{$extras.privateInfo}"&gt;
					&lt;div class="node-stats"&gt;
						&lt;dl class="pairs pairs--rows"&gt;
							&lt;dt&gt;{{ phrase('threads') }}&lt;/dt&gt;
							&lt;dd&gt;{$extras.discussion_count|number_short(1)}&lt;/dd&gt;
						&lt;/dl&gt;
						&lt;dl class="pairs pairs--rows"&gt;
							&lt;dt&gt;{{ phrase('messages') }}&lt;/dt&gt;
							&lt;dd&gt;{$extras.message_count|number_short(1)}&lt;/dd&gt;
						&lt;/dl&gt;
					&lt;/div&gt;
				&lt;/xf:if&gt;

				&lt;div class="node-extra"&gt;
					&lt;xf:if is="{$extras.privateInfo}"&gt;
						&lt;span class="node-extra-placeholder"&gt;{{ phrase('private') }}&lt;/span&gt;
					&lt;xf:elseif is="{$extras.LastThread}" /&gt;
						&lt;div class="node-extra-icon"&gt;
							&lt;xf:if is="$xf.visitor.isIgnoring($extras.last_post_user_id)"&gt;
								&lt;xf:avatar user="{{ null }}" size="xs" /&gt;
							&lt;xf:else /&gt;
								&lt;xf:avatar user="{$extras.LastPostUser}" defaultname="{$extras.last_post_username}" size="xs" /&gt;
							&lt;/xf:if&gt;
						&lt;/div&gt;
						&lt;div class="node-extra-row"&gt;
							&lt;xf:if is="$extras.LastThread.isUnread()"&gt;
								&lt;a href="{{ link('threads/unread', $extras.LastThread) }}" class="node-extra-title" title="{$extras.LastThread.title}"&gt;{{ prefix('thread', $extras.LastThread) }}{$extras.LastThread.title}&lt;/a&gt;
							&lt;xf:else /&gt;
								&lt;a href="{{ link('threads/post', $extras.LastThread, {'post_id': $extras.last_post_id}) }}" class="node-extra-title" title="{$extras.LastThread.title}"&gt;{{ prefix('thread', $extras.LastThread) }}{$extras.LastThread.title}&lt;/a&gt;
							&lt;/xf:if&gt;
						&lt;/div&gt;
						&lt;div class="node-extra-row"&gt;
							&lt;ul class="listInline listInline--bullet"&gt;
								&lt;li&gt;&lt;xf:date time="{$extras.last_post_date}" class="node-extra-date" /&gt;&lt;/li&gt;
								&lt;xf:if is="$xf.visitor.isIgnoring($extras.last_post_user_id)"&gt;
									&lt;li class="node-extra-user"&gt;{{ phrase('ignored_member') }}&lt;/li&gt;
								&lt;xf:else /&gt;
									&lt;li class="node-extra-user"&gt;&lt;xf:username user="{$extras.LastPostUser}" defaultname="{$extras.last_post_username}" /&gt;&lt;/li&gt;
								&lt;/xf:if&gt;
							&lt;/ul&gt;
						&lt;/div&gt;
					&lt;xf:else /&gt;
						&lt;span class="node-extra-placeholder"&gt;{{ phrase('none') }}&lt;/span&gt;
					&lt;/xf:if&gt;
				&lt;/div&gt;
			&lt;/div&gt;
		&lt;/div&gt;

	&lt;/xf:if&gt;

	&lt;xf:if is="{$depth} == 1"&gt;
		&lt;xf:macro id="forum_list::node_list"
			arg-children="{$children}"
			arg-extras="{$childExtras}"
			arg-depth="{{ $depth + 1 }}" /&gt;
	&lt;/xf:if&gt;
&lt;/xf:macro&gt;
</code></pre><p><strong>Save</strong>.</p><blockquote class="ipsQuote" cite="" data-ipsquote=""><div class="ipsQuote_contents" data-ipstruncate=""><p><strong>Penjelasan:</strong> Conditional <code>$depth == 1 OR $depth == 2</code> membuat cover style berlaku untuk forum di root maupun di dalam kategori. Sub-forum (depth ≥ 3) tetap pakai layout original supaya rapi.</p></div></blockquote><hr><h2>9. Tambahkan CSS</h2><p>AdminCP → <strong>Appearance → Style properties → Extra LESS template</strong> → tambahkan blok ini:</p><pre spellcheck="" class="ipsCode language-less" data-language="LESS"><code>/* ============================================
   Intutekno Cover Forum Style
   ============================================ */

.intuteknoCover {
    background: @xf-contentBg;
    border: 1px solid @xf-borderColor;
    border-radius: 8px;
    margin-bottom: 12px;
    overflow: hidden;
    border-top: 5px solid #06b6d4; /* default fallback */
}

.intuteknoCover-body {
    display: flex;
    gap: 0;
}

/* === LEFT: Cover + Info === */
.intuteknoCover-side {
    flex: 0 0 30%;
    max-width: 30%;
    padding: 14px;
    border-right: 1px solid @xf-borderColor;
    background: rgba(0,0,0,0.02);
}

.intuteknoCover-img {
    display: block;
    width: 100%;
    aspect-ratio: 3 / 2;
    overflow: hidden;
    border-radius: 6px;
    margin-bottom: 12px;
}

.intuteknoCover-img img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform .3s ease;
}

.intuteknoCover-img:hover img {
    transform: scale(1.05);
}

.intuteknoCover-title {
    margin: 0 0 8px;
    font-size: 17px;
    font-weight: 700;
}

.intuteknoCover-title a {
    color: @xf-linkColor;
}

.intuteknoCover-desc {
    font-size: 13px;
    color: @xf-textColorMuted;
    line-height: 1.5;
    margin-bottom: 12px;
}

.intuteknoCover-stats {
    display: flex;
    gap: 14px;
    padding-top: 10px;
    border-top: 2px solid #e74c3c;
    font-size: 13px;
}

.intuteknoCover-stats b {
    color: @xf-textColor;
}

/* === RIGHT: Daftar Thread === */
.intuteknoCover-threads {
    flex: 1;
    padding: 8px 14px;
}

.intuteknoThread-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.intuteknoThread-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    border-bottom: 1px solid @xf-borderColorLight;
}

.intuteknoThread-item:last-child {
    border-bottom: 0;
}

.intuteknoThread-item .intuteknoThread-avatar {
    flex: 0 0 32px;
    width: 32px;
    height: 32px;
    margin: 0;
}

.intuteknoThread-item .intuteknoThread-avatar.avatar img,
.intuteknoThread-item .intuteknoThread-avatar img {
    width: 32px !important;
    height: 32px !important;
    border-radius: 50%;
}

.intuteknoThread-item .intuteknoThread-avatar.avatar {
    font-size: 14px !important;
    line-height: 32px !important;
}

.intuteknoThread-main {
    flex: 1;
    min-width: 0;
}

.intuteknoThread-title {
    font-size: 14px;
    margin-bottom: 3px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 500;
}

.intuteknoThread-meta {
    font-size: 12px;
    color: @xf-textColorMuted;
    display: flex;
    gap: 6px;
    align-items: center;
    flex-wrap: wrap;
}

.intuteknoThread-meta .username {
    font-weight: 600;
    color: @xf-linkColor;
}

.intuteknoThread-empty {
    padding: 50px 20px;
    text-align: center;
    color: @xf-textColorMuted;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 12px;
    height: 100%;
    justify-content: center;
}

.intuteknoThread-empty .fa {
    font-size: 36px;
    opacity: 0.25;
}

/* === Responsive Mobile === */
@media (max-width: 768px) {
    .intuteknoCover-body {
        flex-direction: column;
    }
    .intuteknoCover-side,
    .intuteknoCover-threads {
        flex: 1 1 100%;
        max-width: 100%;
        border-right: 0;
        border-bottom: 1px solid @xf-borderColor;
    }
}
</code></pre><p><strong>Save</strong>.</p><hr><h2>10. Setup Cover Image</h2><h3>10.1. Buat folder cover</h3><p>Via File Manager cPanel atau FTP, buat folder:</p><pre spellcheck="" class="ipsCode language-plaintext" data-language="Plain Text"><code>/data/forum_covers/
</code></pre><p>Set permission <code>755</code>.</p><h3>10.2. Cari node_id setiap forum</h3><p>AdminCP → <strong>Forums</strong> → klik nama forum → lihat URL:</p><pre spellcheck="" class="ipsCode language-plaintext" data-language="Plain Text"><code>.../admin.php?nodes/nama-forum.5/edit
                              ↑
                          node_id = 5
</code></pre><h3>10.3. Upload cover image</h3><p>Upload file SVG/WebP/JPG ke <code>/data/forum_covers/</code> dengan nama sesuai node_id:</p><pre spellcheck="" class="ipsCode language-plaintext" data-language="Plain Text"><code>/data/forum_covers/
├── default.svg     ← fallback wajib
├── 5.svg           ← forum dengan node_id 5
├── 6.svg
├── 7.svg
└── ...
</code></pre><p><strong>Spesifikasi cover ideal:</strong></p><ul><li><p>Aspect ratio <strong>3:2</strong></p></li><li><p>Ukuran <strong>600×400 px</strong></p></li><li><p>Format <strong>SVG</strong> (paling ringan dan scalable) atau <strong>WebP</strong></p></li></ul><blockquote class="ipsQuote" cite="" data-ipsquote=""><div class="ipsQuote_contents" data-ipstruncate=""><p><strong>Catatan:</strong> Template di langkah 8 sudah pakai ekstensi <code>.svg</code>. Kalau Anda pakai <code>.webp</code> atau <code>.jpg</code>, ubah dua tempat di template dari <code>.svg</code> menjadi ekstensi yang Anda pakai.</p></div></blockquote><hr><h2>11. Customize Border per Kategori</h2><p>Border-top tiap card forum bisa diwarnai berbeda berdasarkan <strong>kategori induk</strong>-nya. Berkat class <code>intuteknoCover--cat{X}</code> yang otomatis disisipkan di langkah 8, kita tinggal target via CSS.</p><h3>11.1. Cari node_id kategori</h3><p>Sama seperti forum: AdminCP → Forums → klik kategori → lihat node_id di URL.</p><h3>11.2. Tambahkan rules CSS</h3><p>Tambahkan ke Extra LESS:</p><pre spellcheck="" class="ipsCode language-less" data-language="LESS"><code>/* === Border Color per Kategori === */
.intuteknoCover--cat3 { border-top-color: #3b82f6; } /* Tutorial - biru */
.intuteknoCover--cat4 { border-top-color: #f59e0b; } /* Info - amber */
.intuteknoCover--cat5 { border-top-color: #10b981; } /* hijau */
.intuteknoCover--cat6 { border-top-color: #ec4899; } /* pink */
/* tambahkan sesuai kategori yang dimiliki */
</code></pre><p>Sesuaikan angka di <code>--cat{X}</code> dengan node_id kategori, dan warna sesuai branding masing-masing.</p><h3>Save dan refresh</h3><p>Setelah semua perubahan CSS:</p><ol><li><p>Save Extra LESS</p></li><li><p><strong>Tools → Rebuild caches</strong> → centang semua → Rebuild</p></li><li><p>Buka homepage forum → <strong>Ctrl+F5</strong></p></li></ol><hr><h2>12. Troubleshooting</h2><h3>Error <code>Function "latest_forum_threads" is not callable</code></h3><p>Listener belum aktif atau salah class path. Cek:</p><ol><li><p><strong>Add-ons</strong> — add-on Status harus Active</p></li><li><p><strong>Code event listeners</strong> — entry untuk <code>templater_setup</code> harus enabled, Class dan Method harus persis sama dengan file PHP</p></li><li><p><strong>Tools → Rebuild caches</strong> → centang semua</p></li></ol><h3>Cover tidak muncul, ada celah putih di kiri</h3><p>Berarti gambar gagal dimuat. Cek:</p><ol><li><p>File ada di <code>/data/forum_covers/</code>?</p></li><li><p>Permission folder 755, file 644?</p></li><li><p>Nama file sesuai node_id?</p></li><li><p>Path di template sesuai ekstensi file?</p></li><li><p>Ada file <code>default.svg</code> sebagai fallback?</p></li></ol><h3>Layout tidak berubah, masih style asli</h3><ol><li><p>Pastikan template <code>node_list_forum</code> ter-save dengan kode lengkap di langkah 8</p></li><li><p><strong>Rebuild templates</strong> wajib</p></li><li><p><strong>Ctrl+F5</strong> untuk bypass cache browser</p></li><li><p>Cek dengan inspect element — class <code>intuteknoCover</code> harus muncul di div</p></li></ol><h3>Sub-forum jadi style cover juga (tidak diinginkan)</h3><p>Cek conditional di template:</p><pre spellcheck="" class="ipsCode language-plaintext" data-language="Plain Text"><code>&lt;xf:if is="$depth == 1 OR $depth == 2"&gt;
</code></pre><p>Sub-forum biasanya depth ≥ 3, jadi seharusnya tidak ter-cover. Kalau forum Anda strukturnya berbeda, sesuaikan condition.</p><h3>Avatar ada celah ke kiri</h3><p>Pastikan template pakai sintaks ini (avatar inline, bukan dibungkus <code>&lt;a&gt;</code>):</p><pre spellcheck="" class="ipsCode language-plaintext" data-language="Plain Text"><code>&lt;xf:avatar user="{$thread.User}" size="xxs" defaultname="{$thread.username}" class="intuteknoThread-avatar" /&gt;
</code></pre><h3>Class <code>intuteknoCover--cat{id}</code> tidak muncul di HTML</h3><p>Berarti template belum ter-update. Edit ulang, save, dan rebuild templates.</p><hr><h2>13. Penutup</h2><p>Setelah semua langkah di atas, halaman utama forum akan menampilkan card cover untuk setiap forum dengan layout 2 kolom dan border atas warna per kategori.</p><h3>Pengembangan Selanjutnya</h3><p>Beberapa peningkatan yang bisa dilakukan kapan-kapan:</p><ul><li><p><strong>Caching</strong> thread terbaru lewat XF registry supaya tidak query 5 thread per forum setiap homepage di-load</p></li><li><p><strong>Custom field</strong> untuk upload cover via AdminCP (tanpa FTP)</p></li><li><p><strong>Hover effect</strong> halus di card dan row thread</p></li><li><p><strong>Lazy load</strong> cover image dengan blur placeholder</p></li></ul><h3>Struktur File Akhir</h3><pre spellcheck="" class="ipsCode language-plaintext" data-language="Plain Text"><code>/src/addons/Intutekno/CoverForum/
├── addon.json
├── Listener.php
└── TemplateHelper.php

/data/forum_covers/
├── default.svg
├── 5.svg
├── 6.svg
└── ... (sesuai jumlah forum)

AdminCP → Templates: node_list_forum (modified)
AdminCP → Style properties → Extra LESS (modified)
AdminCP → Code event listeners: templater_setup → Listener::templaterSetup
</code></pre><hr><p><strong>Tutorial dibuat berdasarkan implementasi nyata di forum.intutekno.com</strong></p><p>Lisensi: silakan adaptasi dan bagikan ulang. Kalau bermanfaat, sebutkan sumber sebagai <em>amal jariah</em> — pengetahuan yang terus bermanfaat.</p>]]></description><guid isPermaLink="false">4</guid><pubDate>Sun, 17 May 2026 08:30:50 +0000</pubDate></item><item><title>Tutorial Komprehensif Membuat Homepage Kustom di Invision Community 5</title><link>https://lalu.pro/articles/forum-software/tutorial-komprehensif-membuat-homepage-kustom-di-invision-community-5-r1/</link><description><![CDATA[<p>Panduan lengkap berdasarkan pengalaman membangun <strong>lalu.pro</strong> — dengan Invision Community 5.0.18.</p><p><strong>Target:</strong> Pengguna IPS 5 yang ingin membangun homepage editorial dengan data live dari berbagai aplikasi IPS (Forum, Pages, Downloads).  </p><p><strong>Tingkat:</strong> Menengah (butuh dasar HTML/CSS, tidak butuh PHP advance).</p><h2>Prasyarat</h2><p>Sebelum mulai, pastikan Anda punya:</p><p>Sebelum mulai, pastikan Anda punya:</p><p>Teknis</p><ul><li><p><strong>Invision Community 5.0.x</strong> ter-install (panduan ini di-test di 5.0.18)</p></li><li><p><strong>Aplikasi Pages</strong> aktif (wajib — ini yang memungkinkan custom homepage)</p></li><li><p><strong>AdminCP access</strong> sebagai administrator</p></li><li><p><strong>Akses cPanel atau file manager</strong> (untuk backup tema, opsional tapi sangat direkomendasikan)</p></li></ul><p>Pengetahuan</p><ul><li><p>HTML dasar (tag, attribute, nested element)</p></li><li><p>CSS dasar (selector, properties, flexbox, grid)</p></li><li><p>Konsep template tag (mirip Twig, Liquid, atau Blade — tidak perlu PHP)</p></li><li><p>Mengerti perbedaan client-side (HTML/CSS) vs server-side (PHP) rendering</p></li></ul><p>Aplikasi IPS yang Direkomendasikan untuk Live Data</p><ul><li><p><strong>Forum</strong> (untuk fetch topik diskusi)</p></li><li><p><strong>Pages</strong> dengan custom database (untuk artikel)</p></li><li><p><strong>Downloads</strong> (untuk file/resources)</p></li><li><p><strong>Calendar</strong> (untuk events — opsional)</p></li></ul><p>Persiapan Konten</p><ul><li><p>Minimal <strong>3-5 topik forum</strong> sudah dipost</p></li><li><p>Minimal <strong>3-5 artikel</strong> sudah di-publish di Pages database</p></li><li><p>Minimal <strong>2-3 file</strong> sudah di-upload di Downloads</p></li></ul><p>Tanpa konten ini, section live data akan tampak kosong saat testing.</p><h2>Konsep IPS 5 yang Wajib Dipahami</h2><p>Sebelum nyentuh kode, pahami dulu konsep-konsep ini. Banyak frustrasi muncul karena pengguna tidak tahu <strong>vocabulary</strong> IPS 5.</p><h3>1. Pages — Aplikasi Pembuat Halaman</h3><p><strong>Pages </strong>adalah aplikasi IPS yang memungkinkan Anda membuat halaman custom di luar struktur forum standar. Homepage custom Anda akan jadi sebuah Page.</p><p><strong>Path:</strong> <em>AdminCP → Pages</em></p><h3>2. Page Types</h3><p>Saat membuat Page baru, ada dua tipe utama:</p><div class="ipsRichText__table-wrapper"><table style="width: 600px;"><colgroup><col style="width:148px;"><col style="width:297px;"><col style="width:155px;"></colgroup><tbody><tr><th colspan="1" rowspan="1"><p>Tipe</p></th><th colspan="1" rowspan="1"><p>Kapan Dipakai</p></th><th colspan="1" rowspan="1"><p>Tutorial Ini Pakai?</p></th></tr><tr><td colspan="1" rowspan="1"><p>Page Builder</p></td><td colspan="1" rowspan="1"><p>Visual drag-and-drop dengan widget</p></td><td colspan="1" rowspan="1"><p>Tidak</p></td></tr><tr><td colspan="1" rowspan="1"><p>Manual</p></td><td colspan="1" rowspan="1"><p>HTML/template tag custom penuh</p></td><td colspan="1" rowspan="1"><p>Ya</p></td></tr></tbody></table></div><p><strong>Manual</strong> memberi Anda kontrol penuh atas HTML, sementara Page Builder cocok untuk yang tidak mau repot <em>ngoding</em>.</p><h3>3. Use Suite HTML Wrapper — Setting Paling Krusial</h3><p>Ini setting yang sering bikin bingung. Ada dua mode:</p><h4>Mode A: Suite Wrapper ON (Direkomendasikan untuk Production)</h4><p><span class="ipsEmoji" title="">✅</span> Konten Anda di-wrap dalam template global IPS (header, footer, sidebar, scripts)<br><span class="ipsEmoji" title="">✅</span> Security IPS aktif (CSRF, session, auth)<br><span class="ipsEmoji" title="">✅</span> JavaScript dan PHP IPS jalan native<br><span class="ipsEmoji" title="">✅</span> Konsisten dengan halaman lain (forum, artikel detail)<br><span class="ipsEmoji" title="">❌</span> Tidak bisa custom &lt;head&gt;, &lt;body&gt;, atau full HTML</p><p><strong>Kapan pakai:</strong> Saat Anda mau pakai header &amp; footer bawaan IPS, dan hanya custom area konten tengah.</p><h4>Mode B: Suite Wrapper OFF (Full Custom)</h4><p><span class="ipsEmoji" title="">✅</span> Full kontrol dari &lt;!DOCTYPE html&gt; sampai &lt;/html&gt;<br><span class="ipsEmoji" title="">✅</span> Bisa pakai font, CSS, JS dari mana saja<br><span class="ipsEmoji" title="">❌</span> Harus handle security sendiri<br><span class="ipsEmoji" title="">❌</span> Tidak otomatis dapat update template IPS<br><span class="ipsEmoji" title="">❌</span> Header/footer/user dropdown IPS hilang<br><span class="ipsEmoji" title="">❌</span> Login/logout/notifications bawaan IPS tidak otomatis ada</p><p><strong>Kapan pakai:</strong> Saat homepage benar-benar berbeda total dari sisa situs (misal, landing page marketing).</p><blockquote class="ipsQuote" cite="" data-ipsquote=""><div class="ipsQuote_contents" data-ipstruncate=""><p><strong>Rekomendasi panduan ini:</strong> Pakai <strong>Suite Wrapper ON</strong>. Lebih aman, maintenance-friendly, dan transisi antar halaman seamless.</p></div></blockquote><h3>4. Blocks — Mesin Live Data</h3><p>Blocks adalah komponen reusable yang bisa menampilkan data dari aplikasi IPS. Ada <strong>4 tipe block</strong> di IPS 5:</p><div class="ipsRichText__table-wrapper"><table style="width: 636px;"><colgroup><col style="width:119px;"><col style="width:271px;"><col style="width:246px;"></colgroup><tbody><tr><th colspan="1" rowspan="1"><p>Tipe</p></th><th colspan="1" rowspan="1"><p>Fungsi</p></th><th colspan="1" rowspan="1"><p>Contoh Penggunaan</p></th></tr><tr><td colspan="1" rowspan="1"><p>Plugin</p></td><td colspan="1" rowspan="1"><p>Feed otomatis dari aplikasi IPS</p></td><td colspan="1" rowspan="1"><p>Topic Feed, Record Feed, File Feed</p></td></tr><tr><td colspan="1" rowspan="1"><p>Manual HTML</p></td><td colspan="1" rowspan="1"><p>HTML + expression tag + PHP inline</p></td><td colspan="1" rowspan="1"><p>Counter, custom widget</p></td></tr><tr><td colspan="1" rowspan="1"><p>Editor</p></td><td colspan="1" rowspan="1"><p>Konten statis pakai WYSIWYG editor</p></td><td colspan="1" rowspan="1"><p>Welcome message, announcement</p></td></tr><tr><td colspan="1" rowspan="1"><p>Template</p></td><td colspan="1" rowspan="1"><p>Reusable dengan input fields</p></td><td colspan="1" rowspan="1"><p>Multi-purpose container</p></td></tr></tbody></table></div><p>Tutorial ini akan beberapa pakai <strong>Plugin</strong> (untuk feed) dan <strong>Manual HTML</strong> (untuk stats counter).</p><h3>5. Block Templates</h3><p>Tampilan default block IPS sering tidak match dengan desain Anda. Solusinya: <strong>Block Template</strong> — custom template yang bisa Anda terapkan ke block tertentu.</p><p><strong>Path:</strong> <em>AdminCP → Pages → Templates → Add → block template</em></p><p>Block Template menggunakan <strong>template tag IPS</strong> (<code>{{foreach}}, {$variable}</code>, dll) — bukan PHP murni.</p><h3>6. Template Tags vs Expression</h3><p>Dua jenis sintaks yang akan sering Anda lihat:</p><p><strong>Template Tag (logic flow):</strong></p><pre spellcheck="" class="ipsCode language-php-template" data-language="PHP Template"><code>{{if $count &gt; 1000}}
{{foreach $items as $item}}
...
{{endforeach}}
{{endif}}</code></pre><p><strong>Expression (eksekusi PHP):</strong></p><pre spellcheck="" class="ipsCode language-php" data-language="PHP"><code>{expression="number_format($count)"}
{expression="$record-&gt;_title"}</code></pre><p>Aturan: gunakan <code>{{...}}</code> untuk logic, <code>{expression="..."}</code> untuk output PHP.</p><h3>7. Default Page Setting</h3><p>Setelah membuat homepage, Anda harus set sebagai default agar muncul saat user buka root domain (<code>https://situs.com/</code>).</p><p><strong>Path:</strong> <em>AdminCP → System → Settings → Site Features → Default Page</em></p><hr><h2>Fase 1 — Setup Halaman Homepage</h2><h3>Step 1.1: Backup Tema Anda (Wajib!)</h3><p>Sebelum modifikasi apapun, backup tema aktif:</p><p>AdminCP → Customization → Appearance → Themes<br>└─ Hover icon download di samping tema → Save .xml file</p><p>Simpan file <code>.xml</code> ini di komputer lokal. Kalau ada yang rusak, Anda bisa <em>restore</em>.</p><h3>Step 1.2: Buat Page Baru</h3><p><strong>Path:</strong> <em>AdminCP → Pages → Add Page</em></p><p>Pilih Content Editor-nya Manual HTML</p><p><img class="ipsImage ipsRichText__align--block" data-fileid="1" src="https://lalu.pro/uploads/monthly_2026_05/Add_Page_Manual_HTML.webp.ae3e9a92f2e7ae6699c8272ed8a0ec16.webp" alt="Add_Page_Manual_HTML.webp" title="Add_Page_Manual_HTML.webp" loading="lazy"></p><p>Lengkapi Page Details dengan isian seperti dalam tabel di bawah.</p><p><img class="ipsImage ipsImage_thumbnailed ipsRichText__align--block" data-fileid="2" src="https://lalu.pro/uploads/monthly_2026_05/Add_Page_Details.thumb.webp.6d5c6d80f73ba377249a5456d9daac67.webp" alt="Add_Page_Details.webp" title="Add_Page_Details.webp" data-full-image="https://lalu.pro/uploads/monthly_2026_05/Add_Page_Details.webp.83b61743c308e52875748f318e9494e3.webp" loading="lazy"></p><p>Isian mencakup:</p><div class="ipsRichText__table-wrapper"><table style="width: 674px;"><colgroup><col style="width:195px;"><col style="width:208px;"><col style="width:271px;"></colgroup><tbody><tr><th colspan="1" rowspan="1"><p>Field</p></th><th colspan="1" rowspan="1"><p>Isi dengan</p></th><th colspan="1" rowspan="1"><p>Catatan</p></th></tr><tr><td colspan="1" rowspan="1"><p>Page Name</p></td><td colspan="1" rowspan="1"><p>Homepage</p></td><td colspan="1" rowspan="1"><p>Display name internal</p></td></tr><tr><td colspan="1" rowspan="1"><p>Page Filename</p></td><td colspan="1" rowspan="1"><p>home</p></td><td colspan="1" rowspan="1"><p>URL slug (tanpa ekstensi), jika dikosongkan otomatis namanya mengikuti isian Page Name</p></td></tr><tr><td colspan="1" rowspan="1"><p>Page Folder</p></td><td colspan="1" rowspan="1"><p>Select</p></td><td colspan="1" rowspan="1"><p>Biarkan kosong untuk root, centang No Parent</p></td></tr><tr><td colspan="1" rowspan="1"><p>Use suite HTML wrapper</p></td><td colspan="1" rowspan="1"><p>ON (centang)</p></td><td colspan="1" rowspan="1"><p>Critical setting</p></td></tr><tr><td colspan="1" rowspan="1"><p>Page Title</p></td><td colspan="1" rowspan="1"><p>Komunitas Blogger Indonesia</p></td><td colspan="1" rowspan="1"><p>Untuk SEO <code>&lt;title&gt;</code></p></td></tr><tr><td colspan="1" rowspan="1"><p>Content</p></td><td colspan="1" rowspan="1"><p>Kode HTML</p></td><td colspan="1" rowspan="1"><p>Lihat step 1.3</p></td></tr><tr><td colspan="1" rowspan="1"><p>Meta Description</p></td><td colspan="1" rowspan="1"><p>(deskripsi singkat)</p></td><td colspan="1" rowspan="1"><p>Untuk SEO</p></td></tr></tbody></table></div><p>Klik <strong>Save &amp; Edit</strong>.</p><h3>Step 1.3: Struktur HTML Dasar</h3><p>Karena Suite Wrapper ON, <strong>TIDAK</strong> butuh <code>&lt;!DOCTYPE&gt;</code>, <code>&lt;html&gt;</code>, <code>&lt;head&gt;</code>, atau <code>&lt;body&gt;</code>. IPS akan menambahkannya otomatis. Anda cukup tulis konten yang ada di dalam <code>&lt;body&gt;</code> saja.</p><p>Template dasar yang bisa Anda pakai sebagai starting point:</p><pre spellcheck="" class="ipsCode language-xml" data-language="HTML/XML"><code>&lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
&lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&gt;
&lt;link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&amp;family=Geist:wght@300;400;500;600;700&amp;family=Geist+Mono:wght@400;500;600&amp;display=swap" rel="stylesheet"&gt;

&lt;style&gt;
/* Reset &amp; base */
* { margin: 0; padding: 0; box-sizing: border-box; }

:root {
--bg: #ebe7e0;
--ink: #181818;
--accent: #ff5c2b;
/* ... variabel lainnya ... */
}

/* Style section di sini */
&lt;/style&gt;

&lt;!-- HERO SECTION --&gt;
&lt;section class="hero"&gt;
&lt;h1&gt;Selamat datang di komunitas kami&lt;/h1&gt;
&lt;p&gt;Deskripsi singkat tentang komunitas Anda.&lt;/p&gt;
&lt;/section&gt;

&lt;!-- SECTION LAIN --&gt;
&lt;section class="features"&gt;
&lt;!-- ... --&gt;
&lt;/section&gt;</code></pre><p>Itu saja kerangka dasarnya.</p><h3>Step 1.4: Penting — Cara Paste Kode di Editor IPS</h3><p>Editor IPS Pages punya mode Invision HTML Template dan Plain Text.</p><ol><li><p>Klik tab <strong>Content</strong>, secara default terpilih editor Invision HTML Template.</p></li><li><p>Salin kerangka HTML pada langkah 1.3 di atas.</p></li><li><p>Paste di kolom yang tersedia.</p></li><li><p><strong>Save</strong>.</p></li></ol><p>Setelah Save tentukan <strong>View Page</strong>, centang saja semua agar homepage dapat dilihat oleh semua pengunjung.</p><h3>Step 1.5: Set Homepage Sebagai Default</h3><p>Setelah halaman terbuat, jadikan default, langsung saja klik ikon bintang, lalu geser tombol <strong>Set as default</strong> page ke kanan (ON). Klik Save. Sekarang akses <code>https://situs-anda.com/</code> — homepage Anda muncul.</p><p>Catatan: Jangan lupa atur aplikasi Pages menjadi default juga.</p><h3>Step 1.6: Verifikasi</h3><p>Buka homepage di browser baru (incognito) untuk pastikan tampil benar:</p><ul><li><p>Tampil tanpa error</p></li><li><p>Header &amp; footer IPS muncul (karena Suite Wrapper ON)</p></li><li><p>Konten custom Anda di tengah</p></li><li><p>Font Google Fonts loaded (cek dengan Inspector → Network)</p></li></ul><p>Sampai di sini Anda sudah berhasil membuat homepage. Tapi masih sangat sederhana, hanya berisi teks selamat datang. Selanjutnya kita akan menambahkan beberapa block live.</p><h2>Fase 2 — Live Data Integration</h2><p>Bagian ini yang membuat homepage Anda <strong>hidup</strong> — menampilkan data real dari forum, artikel, dan files.</p><h3>2.1 Strategi Konversi Bertahap</h3><p>Urutan pekerjaan saya saat membuat homepage <a rel="external nofollow" href="https://lalu.pro">lalu.pro</a> adalah per section.<br></p><div class="ipsRichText__table-wrapper"><table style="min-width: 649px;"><colgroup><col style="min-width:20px;"><col style="width:227px;"><col style="width:178px;"><col style="width:224px;"></colgroup><tbody><tr><th colspan="1" rowspan="1"><p>Urutan</p></th><th colspan="1" rowspan="1"><p>Section</p></th><th colspan="1" rowspan="1"><p>Tipe Block</p></th><th colspan="1" rowspan="1"><p>Tingkat Kesulitan</p></th></tr><tr><td colspan="1" rowspan="1"><p>1</p></td><td colspan="1" rowspan="1"><p>Stats counter (member, topic, artikel)</p></td><td colspan="1" rowspan="1"><p>Manual HTML</p></td><td colspan="1" rowspan="1"><p>Mudah</p></td></tr><tr><td colspan="1" rowspan="1"><p>2</p></td><td colspan="1" rowspan="1"><p>Latest articles</p></td><td colspan="1" rowspan="1"><p>Plugin (Record Feed)</p></td><td colspan="1" rowspan="1"><p>Sedang</p></td></tr><tr><td colspan="1" rowspan="1"><p>3</p></td><td colspan="1" rowspan="1"><p>Hot topics</p></td><td colspan="1" rowspan="1"><p>Plugin (Topic Feed)</p></td><td colspan="1" rowspan="1"><p>Sedang</p></td></tr><tr><td colspan="1" rowspan="1"><p>4</p></td><td colspan="1" rowspan="1"><p>Latest files (Download)</p></td><td colspan="1" rowspan="1"><p>Plugin (File Feed)</p></td><td colspan="1" rowspan="1"><p>Sedang</p></td></tr></tbody></table></div><p>Tapi saya hanya akan memandu Anda membuat <em>stat counter</em> dulu agar Anda melihat tampilannya dan memahmi alur kerjanya.</p><h3>2.2 Stats Counter — Member Count</h3><p>Kita perlu membuat sebuah block.</p><p><strong>Path:</strong> <em>AdminCP → Pages → Blocks → Create New Block → pilih "Manual HTML</em></p><p><img class="ipsImage ipsRichText__align--block" data-fileid="3" src="https://lalu.pro/uploads/monthly_2026_05/Create_New_Block.webp.4b1923dbbf4c0330c732fb574b626e27.webp" alt="Create_New_Block.webp" title="" loading="lazy"></p><p>Setelah klik tombol Next, isi form <strong>Details</strong> dan <strong>Content</strong>.</p><p><img class="ipsImage ipsRichText__align--block" data-fileid="4" src="https://lalu.pro/uploads/monthly_2026_05/Details_block.webp.01fd7cc122aaaa5a58076cd6c0995afe.webp" alt="Details_block.webp" title="" width="764" height="442" loading="lazy"></p><p><img class="ipsImage ipsRichText__align--block" data-fileid="6" src="https://lalu.pro/uploads/monthly_2026_05/Block_content.webp.5f27c0c45edc6af20a854ffaac5c6b3e.webp" alt="Block_content.webp" title="" loading="lazy"></p><p><strong>Title:</strong> Stat Members Count<br><strong>Template Key:</strong> <em>lalu_stat_members</em>, (tanpa spasi)<br><strong>Description:</strong> (boleh kosong)<br><strong>Content (paste persis):</strong></p><pre spellcheck="" class="ipsCode language-xml" data-language="HTML/XML"><code>{expression="number_format( \IPS\Db::i()-&gt;select( 'COUNT(*)', 'core_members', array( 'completed=?', 1 ) )-&gt;first() )"}</code></pre><p>Penjelasan kode:</p><ul><li><p><code>\IPS\Db::i()</code> — akses database IPS</p></li><li><p><code>select('COUNT(*)', 'core_members'...)</code> — hitung jumlah member</p></li><li><p><code>'completed=?', 1</code> — hanya yang sudah verifikasi email</p></li><li><p><code>number_format(...)</code> — format dengan koma (2,847)</p></li></ul><p>Klik <strong>Save</strong>. Klik Save sekali lagi (set view page).</p><h3>2.3 Stats Counter — Topic Count</h3><p>Sekarang buat block baru lagi, langkah-langkahnya seperti block member counter.</p><p>Isiannya:</p><p><strong>Title:</strong> Stat Topics Count<br><strong>Template Key:</strong> <em>lalu_stat_topics</em><br><strong>Content:</strong></p><pre spellcheck="" class="ipsCode language-xml" data-language="HTML/XML"><code>{{$count = \IPS\Db::i()-&gt;select( 'COUNT(*)', 'forums_topics', array( 'approved=?', 1 ) )-&gt;first();}}
{{if $count &gt;= 1000}}
{expression="round( $count / 1000, 1 )"}&lt;em&gt;k&lt;/em&gt;
{{else}}
{expression="number_format( $count )"}
{{endif}}</code></pre><p>Penjelasan:</p><ul><li><p>Hitung topic yang approved (approved=1)</p></li><li><p>Kalau ≥1000, tampilkan format 14k, 1.5k</p></li><li><p>Kalau &lt;1000, tampilkan angka penuh</p></li></ul><h3>2.4 Stats Counter — Article Count</h3><p>Cek dulu Database ID Articles:</p><p>AdminCP → Pages → Databases<br>└─ Cari "Articles" → catat angka ID di kolom paling kiri</p><p>Misal ID = <code>1</code>, maka tabel-nya adalah <code>cms_custom_database_1</code>.</p><p>Buat block dengan isian berikut:</p><p><strong>Title:</strong> Stat Articles Count<br><strong>Template Key:</strong> lalu_stat_articles<br><strong>Content (ganti angka 1 dengan ID Anda):</strong></p><pre spellcheck="" class="ipsCode language-xml" data-language="HTML/XML"><code>{expression="number_format( \IPS\Db::i()-&gt;select( 'COUNT(*)', 'cms_custom_database_1', array( 'record_approved=?', 1 ) )-&gt;first() )"}</code></pre><h3>2.5 Pasang Stats Block di Homepage</h3><p>Di Page Content homepage, di tempat yang sesuai:</p><pre spellcheck="" class="ipsCode language-xml" data-language="HTML/XML"><code>&lt;div class="hero-stats"&gt;
&lt;div class="stat"&gt;
&lt;div class="stat-num"&gt;{block="lalu_stat_members"}&lt;/div&gt;
&lt;div class="stat-label"&gt;Member aktif&lt;/div&gt;
&lt;/div&gt;
&lt;div class="stat"&gt;
&lt;div class="stat-num"&gt;{block="lalu_stat_topics"}&lt;/div&gt;
&lt;div class="stat-label"&gt;Diskusi&lt;/div&gt;
&lt;/div&gt;
&lt;div class="stat"&gt;
&lt;div class="stat-num"&gt;{block="lalu_stat_articles"}&lt;/div&gt;
&lt;div class="stat-label"&gt;Tutorial&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</code></pre><p>Anda bisa mencoba kode yang siap pakai berikut:</p><pre spellcheck="" class="ipsCode language-xml" data-language="HTML/XML"><code>&lt;style&gt;
/* Reset &amp; base */
* { margin: 0; padding: 0; box-sizing: border-box; }

:root {
--bg: #ebe7e0;
--ink: #181818;
--accent: #ff5c2b;
--muted: #6b6b6b;
--card: #ffffff;
--radius: 12px;
--gap: 1.5rem;
}

body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: var(--bg);
color: var(--ink);
line-height: 1.6;
padding: 2rem 1rem;
}

/* HERO */
.hero {
max-width: 960px;
margin: 0 auto 3rem;
text-align: center;
padding: 3rem 1.5rem;
}

.hero h1 {
font-size: clamp(1.75rem, 4vw, 2.75rem);
font-weight: 700;
line-height: 1.2;
margin-bottom: 1rem;
}

.hero h1::after {
content: "";
display: block;
width: 60px;
height: 4px;
background: var(--accent);
margin: 1rem auto 0;
border-radius: 2px;
}

.hero p {
font-size: 1.1rem;
color: var(--muted);
max-width: 600px;
margin: 0 auto 2.5rem;
}

/* Hero stats */
.hero-stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--gap);
max-width: 700px;
margin: 0 auto;
}

.stat {
background: var(--card);
padding: 1.5rem 1rem;
border-radius: var(--radius);
border: 1px solid rgba(0, 0, 0, 0.06);
transition: transform 0.2s ease;
}

.stat:hover {
transform: translateY(-3px);
}

.stat-num {
font-size: 2rem;
font-weight: 700;
color: var(--accent);
line-height: 1;
margin-bottom: 0.5rem;
}

.stat-label {
font-size: 0.9rem;
color: var(--muted);
}

/* Features section */
.features {
max-width: 960px;
margin: 0 auto;
padding: 2rem 1rem;
}

/* Responsive */
@media (max-width: 600px) {
.hero-stats {
grid-template-columns: 1fr;
}

.hero {
padding: 2rem 1rem;
}
}
&lt;/style&gt;

&lt;!-- HERO SECTION --&gt;
&lt;section class="hero"&gt;
&lt;h1&gt;Selamat datang di komunitas kami&lt;/h1&gt;
&lt;p&gt;Deskripsi singkat tentang komunitas Anda.&lt;/p&gt;

&lt;div class="hero-stats"&gt;
&lt;div class="stat"&gt;
&lt;div class="stat-num"&gt;{block="lalu_stat_members"}&lt;/div&gt;
&lt;div class="stat-label"&gt;Member aktif&lt;/div&gt;
&lt;/div&gt;
&lt;div class="stat"&gt;
&lt;div class="stat-num"&gt;{block="lalu_stat_topics"}&lt;/div&gt;
&lt;div class="stat-label"&gt;Diskusi&lt;/div&gt;
&lt;/div&gt;
&lt;div class="stat"&gt;
&lt;div class="stat-num"&gt;{block="lalu_stat_articles"}&lt;/div&gt;
&lt;div class="stat-label"&gt;Tutorial&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;</code></pre><p>Save → Clear cache → Refresh.</p><p>Lihat hasilnya, harusnya seperti berikut:</p><p><img class="ipsImage ipsRichText__align--block" data-fileid="5" src="https://lalu.pro/uploads/monthly_2026_05/Membuat_homepage_kustom_ips_v5.webp.9479a1a1d9456c28cc881e25425d0a04.webp" alt="Membuat_homepage_kustom_ips_v5.webp" title="" loading="lazy"></p><p>Saya harap Anda memahami kerjanya dan bisa berkreasi.</p>]]></description><guid isPermaLink="false">1</guid><pubDate>Fri, 15 May 2026 23:37:35 +0000</pubDate></item></channel></rss>
