Everything posted by Lalu
-
Bagaimana mengatasi White Screen of Death (WSoD)?
Bagaimana cara mengatasi White Screen of Death (WSoD)?
- Apakah Blog sudah mati?
-
Nitro Blogger Template
- Version 1.0
Nitro adalah tema Blogger bergaya reader / dokumentasi dengan tata letak tiga kolom: sidebar navigasi gelap di kiri, daftar artikel (Daftar Isi) di tengah, dan area baca yang lapang di kanan. Dirancang untuk blog informasi dan panduan yang mengutamakan keterbacaan, kecepatan, dan navigasi yang mulus. Dikembangkan oleh Lalu Abd. Rahman. Fitur dan kelebihannya: Tata letak 3 kolom adaptif — sidebar, daftar isi, dan konten yang menyusut rapi menjadi satu kolom di perangkat seluler, lengkap dengan menu hamburger dan overlay. Mode Terang & Gelap — sakelar tema di topbar dengan preferensi tersimpan otomatis di perangkat pengunjung. Panel sticky — sidebar, daftar artikel, dan blok widget kanan tetap diam saat konten digulir, sehingga navigasi selalu terjangkau. Daftar Isi otomatis — TOC di dalam pos dibuat dari heading H2/H3 dengan smooth scroll, ditambah estimasi waktu baca dan penanda tanggal terbit/diperbarui. Pencarian instan — filter artikel sisi-klien yang langsung menyaring daftar tanpa memuat ulang halaman. Widget kaya — kartu profil penulis, postingan populer bernomor, dan statistik blog (total pos, komentar, tayangan) yang ditarik dari feed Blogger. Typografi premium — keluarga IBM Plex (Sans, Serif, Mono, Arabic) untuk antarmuka, badan tulisan, kode, dan teks RTL. Performa & efisiensi — daftar artikel pada halaman pos dibatasi 30 entri terbaru, komentar dimuat lazy lewat IntersectionObserver, dan thumbnail rusak disembunyikan otomatis. SEO terintegrasi — Open Graph, Twitter Card, kanonik, serta data terstruktur JSON-LD (WebSite + BlogPosting) agar tampilan berbagi dan hasil pencarian lebih kaya. Sentuhan detail — breadcrumb dinamis, scrollbar tipis seragam di semua peramban, halaman 404 dan kontak khusus, serta gaya lengkap untuk tabel, kutipan, blok kode, kotak info/peringatan, dan tombol. Demo$10 -
Kuis Berhadiah Ke-1
until
Tersedia hadiah uang tunai 50 ribu rupiah bagi anggota yang menyelesaikan kuis dengan skor dan waktu terbaik. Topik kuis adalah 'SEO untuk Pemula'. Hadiah akan dikirimkan via transfer bank. Syarat dan ketentuan: Belum pernah mendapatkan hadiah dari kami. Kuis hanya dapat diikuti oleh anggota terdaftar. Hadiah tidak dapat ditukar dengan apapun. Komplain tidak diterima jika status transfer dari kami telah berhasil, termasuk komplain salah nomor rekening, gangguan jaringan, dlsb. Catatan: Insyaallah link kuis akan tersedia 25 Mei 2026. Semoga berhasil! -
Tutorial: Membuat Cover Forum Style IPS untuk XenForo
Panduan lengkap membuat cover forum XenForo yang tampialannya mirip dengan IPS. Konsep tampilan akhir: cover di kiri, daftar thread terbaru di kanan, border atas warna berbeda per kategori. Mau coba? View full article
-
Tutorial: Membuat Cover Forum Style IPS untuk XenForo
Panduan lengkap membangun tampilan cover forum 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. Estimasi waktu: 30–45 menit Tingkat: Menengah (perlu nyaman edit file PHP via FTP dan template XenForo) Konsep tampilan akhir: cover di kiri, daftar thread terbaru di kanan, border atas warna berbeda per kategori. Lihat demo: https://forum.intutekno.com/ 1. Yang Akan DibuatSetiap forum di halaman utama akan menampilkan: Kolom kiri (30% lebar): Cover image 3:2 (SVG/WebP/JPG) Judul forum (link ke forum) Deskripsi forum Statistik (jumlah threads & messages) Kolom kanan (70% lebar): 5 thread terbaru dengan avatar, judul, username, tanggal, jumlah replies Atau ikon + pesan "Belum ada thread" kalau forum masih kosong Bonus: Border-top tiap card forum bisa diwarnai berbeda berdasarkan kategori induk-nya. 2. PrasyaratXenForo 2.3.x (panduan ini diuji di 2.3.10) Akses AdminCP Akses FTP/File Manager ke server (untuk upload PHP files) Akses cPanel atau setara untuk backup database (opsional tapi disarankan) Pemahaman dasar HTML/CSS akan membantu 3. Backup DuluSebelum apa pun, lakukan dua backup: Backup database: cPanel → phpMyAdmin → pilih database forum → Export → Quick → Go Backup file: File Manager cPanel → kompres folder forum jadi .zip → download Kalau ada yang salah saat ngoprek, kita bisa balik ke kondisi awal. 4. Aktifkan Development ModeBuka /src/config.php lewat File Manager atau FTP. Tambahkan dua baris ini di paling bawah: $config['development']['enabled'] = true; $config['development']['defaultAddOn'] = 'Intutekno/CoverForum'; Ganti Intutekno/CoverForum sesuai nama yang Anda inginkan (format: VendorName/AddOnName). Setelah save, refresh AdminCP — menu Development dan Add-ons akan muncul di sidebar. 5. Bikin Add-on PHPAdd-on ini bertugas menjalankan satu fungsi: mengambil 5 thread terbaru dari sebuah forum dan menyediakannya untuk dipakai di template. 5.1. Buat folder add-onVia FTP, buat struktur folder: /src/addons/Intutekno/CoverForum/ 5.2. Buat file addon.jsonIsi: { "title": "Intutekno Cover Forum", "version_string": "1.0.0", "version_id": 1000070, "dev": "Intutekno", "supported": true } 5.3. Buat file Listener.php<?php namespace Intutekno\CoverForum; class Listener { public static function templaterSetup(\XF\Container $container, \XF\Template\Templater &$templater) { $templater->addFunction('latest_forum_threads', ['Intutekno\CoverForum\TemplateHelper', 'templaterFunction'] ); } } 5.4. Buat file TemplateHelper.php<?php namespace Intutekno\CoverForum; class TemplateHelper { public static function templaterFunction($templater, &$escape, $nodeId, $limit = 5) { $escape = false; return self::getLatestThreads($nodeId, $limit); } public static function getLatestThreads($nodeId, $limit = 5) { $finder = \XF::finder('XF:Thread') ->where('node_id', $nodeId) ->where('discussion_state', 'visible') ->where('discussion_type', '<>', 'redirect') ->with('User') ->with('Prefix') ->order('last_post_date', 'DESC') ->limit($limit); return $finder->fetch(); } } Struktur akhir folder: /src/addons/Intutekno/CoverForum/ ├── addon.json ├── Listener.php └── TemplateHelper.php 6. Install Add-onDi AdminCP: Klik menu Add-ons di sidebar Cari "Intutekno Cover Forum" di list Klik tombol Install di sampingnya Confirm Status add-on harus jadi Active (toggle hijau). 7. Register Code Event ListenerIni langkah yang menghubungkan add-on PHP kita ke template engine XenForo. AdminCP → Development → Code event listeners Klik + Add code event listener di pojok kanan atas Isi form: Field Isi Listen to event templater_setup Event hint (kosongkan) Execute callback: Class Intutekno\CoverForum\Listener Execute callback: Method templaterSetup Callback execution order 10 (default) Enable callback execution ✅ centang Description Register latest_forum_threads function Add-on Intutekno Cover Forum 1.0.0 Klik Save 8. Edit Template node_list_forumAdminCP → Appearance → Templates → cari template node_list_forum → Edit. Ganti seluruh isi template dengan kode berikut: <xf:macro id="depth1" arg-node="!" arg-extras="!" arg-children="!" arg-childExtras="!" arg-depth="1"> <div class="block"> <div class="block-container"> <div class="block-body"> <xf:macro id="forum" arg-node="{$node}" arg-extras="{$extras}" arg-children="{$children}" arg-childExtras="{$childExtras}" arg-depth="{$depth}" /> </div> </div> </div> </xf:macro> <xf:macro id="depth2" arg-node="!" arg-extras="!" arg-children="!" arg-childExtras="!" arg-depth="1"> <xf:macro id="forum" arg-node="{$node}" arg-extras="{$extras}" arg-children="{$children}" arg-childExtras="{$childExtras}" arg-depth="{$depth}" /> </xf:macro> <xf:macro id="depthN" arg-node="!" arg-extras="!" arg-children="!" arg-childExtras="!" arg-depth="1"> <li> <a href="{{ link('forums', $node) }}" class="subNodeLink subNodeLink--forum {{ $extras.hasNew ? 'subNodeLink--unread' : '' }}"> <xf:fa icon="{{ $node.Data.TypeHandler.getTypeIconClass() ?: 'fa-comments' }}" class="subNodeLink-icon" />{$node.title} </a> <xf:macro id="forum_list::sub_node_list" arg-children="{$children}" arg-childExtras="{$childExtras}" arg-depth="{{ $depth + 1 }}" /> </li> </xf:macro> <xf:macro id="forum" arg-node="!" arg-extras="!" arg-children="!" arg-childExtras="!" arg-depth="!" arg-chooseName="" arg-bonusInfo=""> <xf:if is="$depth == 1 OR $depth == 2"> <!-- ===== COVER STYLE (forum utama) ===== --> <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' }}"> <div class="intuteknoCover-body"> <div class="intuteknoCover-side"> <a href="{{ link('forums', $node) }}" class="intuteknoCover-img"> <img src="/data/forum_covers/{$node.node_id}.svg" onerror="this.src='/data/forum_covers/default.svg'" alt="{$node.title}" loading="lazy" /> </a> <h3 class="intuteknoCover-title"> <a href="{{ link('forums', $node) }}">{$node.title}</a> </h3> <xf:if is="$node.description"> <div class="intuteknoCover-desc">{$node.description|raw}</div> </xf:if> <div class="intuteknoCover-stats"> <span><b>{{ phrase('threads') }}:</b> {$extras.discussion_count|number_short(1)}</span> <span><b>{{ phrase('messages') }}:</b> {$extras.message_count|number_short(1)}</span> </div> </div> <div class="intuteknoCover-threads"> <xf:set var="$latest" value="{{ latest_forum_threads($node.node_id, 5) }}" /> <xf:if is="$latest is not empty"> <ul class="intuteknoThread-list"> <xf:foreach loop="$latest" value="$thread"> <li class="intuteknoThread-item"> <xf:avatar user="{$thread.User}" size="xxs" defaultname="{$thread.username}" class="intuteknoThread-avatar" /> <div class="intuteknoThread-main"> <div class="intuteknoThread-title"> <xf:if is="$thread.prefix_id">{{ prefix('thread', $thread) }}</xf:if> <a href="{{ link('threads', $thread) }}">{$thread.title}</a> </div> <div class="intuteknoThread-meta"> <a href="{{ link('members', $thread.User) }}" class="username">{$thread.username}</a> <span>·</span> <xf:date time="{$thread.last_post_date}" /> <span>·</span> <span><xf:fa icon="fa-comment-alt" /> {$thread.reply_count|number_short(1)}</span> </div> </div> </li> </xf:foreach> </ul> <xf:else /> <div class="intuteknoThread-empty"> <xf:fa icon="fa-comments" /> <span>Belum ada thread di forum ini</span> </div> </xf:if> </div> </div> </div> <xf:else /> <!-- ===== ORIGINAL STYLE (sub-forum, depth >= 3) ===== --> <div class="node node--id{$node.node_id} node--depth{$depth} node--forum {{ $extras.hasNew ? 'node--unread' : 'node--read' }}"> <div class="node-body"> <span class="node-icon" aria-hidden="true"> <xf:fa icon="{{ $node.Data.TypeHandler.getTypeIconClass() ?: 'fa-comments' }}" /> </span> <div class="node-main js-nodeMain"> <xf:if is="$chooseName"> <xf:checkbox standalone="true"> <xf:option labelclass="u-pullRight" class="js-chooseItem" name="{$chooseName}[]" value="{$node.node_id}" /> </xf:checkbox> </xf:if> <xf:set var="$descriptionDisplay" value="{{ property('nodeListDescriptionDisplay') }}" /> <h3 class="node-title"> <a href="{{ link('forums', $node) }}" data-xf-init="{{ $descriptionDisplay == 'tooltip' ? 'element-tooltip' : '' }}" data-shortcut="node-description">{$node.title}</a> </h3> <xf:if is="$descriptionDisplay != 'none' && $node.description"> <div class="node-description {{ $descriptionDisplay == 'tooltip' ? 'node-description--tooltip js-nodeDescTooltip' : '' }}">{$node.description|raw}</div> </xf:if> <div class="node-meta"> <xf:if is="!{$extras.privateInfo}"> <div class="node-statsMeta"> <dl class="pairs pairs--inline"> <dt>{{ phrase('threads') }}</dt> <dd>{$extras.discussion_count|number_short(1)}</dd> </dl> <dl class="pairs pairs--inline"> <dt>{{ phrase('messages') }}</dt> <dd>{$extras.message_count|number_short(1)}</dd> </dl> </div> </xf:if> <xf:if is="$depth == 2 AND property('nodeListSubDisplay') == 'menu'"> <xf:macro id="forum_list::sub_nodes_menu" arg-children="{$children}" arg-childExtras="{$childExtras}" arg-depth="{{ $depth + 1 }}" /> </xf:if> </div> <xf:if is="$depth == 2 AND property('nodeListSubDisplay') == 'flat'"> <xf:macro id="forum_list::sub_nodes_flat" arg-children="{$children}" arg-childExtras="{$childExtras}" arg-depth="{{ $depth + 1 }}" /> </xf:if> <xf:if is="$bonusInfo is not empty"> <div class="node-bonus">{$bonusInfo}</div> </xf:if> </div> <xf:if is="!{$extras.privateInfo}"> <div class="node-stats"> <dl class="pairs pairs--rows"> <dt>{{ phrase('threads') }}</dt> <dd>{$extras.discussion_count|number_short(1)}</dd> </dl> <dl class="pairs pairs--rows"> <dt>{{ phrase('messages') }}</dt> <dd>{$extras.message_count|number_short(1)}</dd> </dl> </div> </xf:if> <div class="node-extra"> <xf:if is="{$extras.privateInfo}"> <span class="node-extra-placeholder">{{ phrase('private') }}</span> <xf:elseif is="{$extras.LastThread}" /> <div class="node-extra-icon"> <xf:if is="$xf.visitor.isIgnoring($extras.last_post_user_id)"> <xf:avatar user="{{ null }}" size="xs" /> <xf:else /> <xf:avatar user="{$extras.LastPostUser}" defaultname="{$extras.last_post_username}" size="xs" /> </xf:if> </div> <div class="node-extra-row"> <xf:if is="$extras.LastThread.isUnread()"> <a href="{{ link('threads/unread', $extras.LastThread) }}" class="node-extra-title" title="{$extras.LastThread.title}">{{ prefix('thread', $extras.LastThread) }}{$extras.LastThread.title}</a> <xf:else /> <a href="{{ link('threads/post', $extras.LastThread, {'post_id': $extras.last_post_id}) }}" class="node-extra-title" title="{$extras.LastThread.title}">{{ prefix('thread', $extras.LastThread) }}{$extras.LastThread.title}</a> </xf:if> </div> <div class="node-extra-row"> <ul class="listInline listInline--bullet"> <li><xf:date time="{$extras.last_post_date}" class="node-extra-date" /></li> <xf:if is="$xf.visitor.isIgnoring($extras.last_post_user_id)"> <li class="node-extra-user">{{ phrase('ignored_member') }}</li> <xf:else /> <li class="node-extra-user"><xf:username user="{$extras.LastPostUser}" defaultname="{$extras.last_post_username}" /></li> </xf:if> </ul> </div> <xf:else /> <span class="node-extra-placeholder">{{ phrase('none') }}</span> </xf:if> </div> </div> </div> </xf:if> <xf:if is="{$depth} == 1"> <xf:macro id="forum_list::node_list" arg-children="{$children}" arg-extras="{$childExtras}" arg-depth="{{ $depth + 1 }}" /> </xf:if> </xf:macro> Save. 9. Tambahkan CSSAdminCP → Appearance → Style properties → Extra LESS template → tambahkan blok ini: /* ============================================ 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; } } Save. 10. Setup Cover Image10.1. Buat folder coverVia File Manager cPanel atau FTP, buat folder: /data/forum_covers/ Set permission 755. 10.2. Cari node_id setiap forumAdminCP → Forums → klik nama forum → lihat URL: .../admin.php?nodes/nama-forum.5/edit ↑ node_id = 5 10.3. Upload cover imageUpload file SVG/WebP/JPG ke /data/forum_covers/ dengan nama sesuai node_id: /data/forum_covers/ ├── default.svg ← fallback wajib ├── 5.svg ← forum dengan node_id 5 ├── 6.svg ├── 7.svg └── ... Spesifikasi cover ideal: Aspect ratio 3:2 Ukuran 600×400 px Format SVG (paling ringan dan scalable) atau WebP 11. Customize Border per KategoriBorder-top tiap card forum bisa diwarnai berbeda berdasarkan kategori induk-nya. Berkat class intuteknoCover--cat{X} yang otomatis disisipkan di langkah 8, kita tinggal target via CSS. 11.1. Cari node_id kategoriSama seperti forum: AdminCP → Forums → klik kategori → lihat node_id di URL. 11.2. Tambahkan rules CSSTambahkan ke Extra LESS: /* === 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 */ Sesuaikan angka di --cat{X} dengan node_id kategori, dan warna sesuai branding masing-masing. Save dan refreshSetelah semua perubahan CSS: Save Extra LESS Tools → Rebuild caches → centang semua → Rebuild Buka homepage forum → Ctrl+F5 12. TroubleshootingError Function "latest_forum_threads" is not callableListener belum aktif atau salah class path. Cek: Add-ons — add-on Status harus Active Code event listeners — entry untuk templater_setup harus enabled, Class dan Method harus persis sama dengan file PHP Tools → Rebuild caches → centang semua Cover tidak muncul, ada celah putih di kiriBerarti gambar gagal dimuat. Cek: File ada di /data/forum_covers/? Permission folder 755, file 644? Nama file sesuai node_id? Path di template sesuai ekstensi file? Ada file default.svg sebagai fallback? Layout tidak berubah, masih style asliPastikan template node_list_forum ter-save dengan kode lengkap di langkah 8 Rebuild templates wajib Ctrl+F5 untuk bypass cache browser Cek dengan inspect element — class intuteknoCover harus muncul di div Sub-forum jadi style cover juga (tidak diinginkan)Cek conditional di template: <xf:if is="$depth == 1 OR $depth == 2"> Sub-forum biasanya depth ≥ 3, jadi seharusnya tidak ter-cover. Kalau forum Anda strukturnya berbeda, sesuaikan condition. Avatar ada celah ke kiriPastikan template pakai sintaks ini (avatar inline, bukan dibungkus <a>): <xf:avatar user="{$thread.User}" size="xxs" defaultname="{$thread.username}" class="intuteknoThread-avatar" /> Class intuteknoCover--cat{id} tidak muncul di HTMLBerarti template belum ter-update. Edit ulang, save, dan rebuild templates. 13. PenutupSetelah semua langkah di atas, halaman utama forum akan menampilkan card cover untuk setiap forum dengan layout 2 kolom dan border atas warna per kategori. Pengembangan SelanjutnyaBeberapa peningkatan yang bisa dilakukan kapan-kapan: Caching thread terbaru lewat XF registry supaya tidak query 5 thread per forum setiap homepage di-load Custom field untuk upload cover via AdminCP (tanpa FTP) Hover effect halus di card dan row thread Lazy load cover image dengan blur placeholder Struktur File Akhir/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 Tutorial dibuat berdasarkan implementasi nyata di forum.intutekno.com Lisensi: silakan adaptasi dan bagikan ulang. Kalau bermanfaat, sebutkan sumber sebagai amal jariah — pengetahuan yang terus bermanfaat.
-
Cara Membuat PageSpeed Tester Sendiri untuk Blog
Tutorial lengkap membuat PageSpeed Tester di Blogger, bisa Cek skor performa, SEO, accessibility, dan Core Web Vitals. Powered by Google PageSpeed Insights. View full article
-
Cara Membuat PageSpeed Tester Sendiri untuk Blog
Tutorial lengkap membuat tool PageSpeed Tester yang aman dan gratis untuk blog Blogger Anda. Tool ini menggunakan Google PageSpeed Insights API dengan Cloudflare Worker sebagai proxy untuk menyembunyikan API key. Apa yang Akan Anda BuatTool yang bisa: Cek skor Performance, Accessibility, Best Practices, dan SEO halaman manapun Tampilkan Core Web Vitals (FCP, LCP, TBT, CLS, dll) Berikan rekomendasi perbaikan diurutkan berdasarkan impact Cache hasil 1 jam untuk hemat quota 100% gratis (Google API quota gratis cukup untuk blog normal) Mengapa Butuh Cloudflare Worker?API Google butuh API key untuk akses unlimited. Kalau key disimpan langsung di JavaScript blog Anda: Key terlihat di view-source dan Network tab browser Siapa saja bisa copy key Anda Pakai untuk panggil API mereka sendiri Quota Anda habis, atau worse — kena billing kalau key Anda attach ke service berbayar Solusi: Simpan key di Cloudflare Worker (server). Tool browser hanya panggil Worker, Worker yang panggil Google API. Browser (tool Anda) → Cloudflare Worker (key di sini) → Google PageSpeed API Key tidak pernah keluar dari server. Aman. PrasyaratSebelum mulai, siapkan: Akun Google (untuk Google Cloud Console) Akun Cloudflare (gratis, daftar di cloudflare.com) Blog Blogger Anda Tidak perlu credit card untuk setup ini. STEP 1: Dapatkan Google API Key1.1 Aktifkan PageSpeed Insights APIBuka Google Cloud Console Buat project baru (atau pilih yang sudah ada) Di sidebar kiri: APIs & Services → Library Search: "PageSpeed Insights API" Klik hasil, lalu Enable 1.2 Buat API KeyAPIs & Services → Credentials Create Credentials → API key Key baru muncul. Copy key tersebut dan simpan sementara di Notepad/text editor Klik Edit API key untuk setup restriction 1.3 Set Restriction (Sangat Penting!)Tanpa restriction, key bisa dipakai siapa saja yang punya key tersebut. Application restrictions: Pilih Websites Klik Add an item Isi domain Anda: *.laluabdrahman.com* (ganti dengan domain Anda) https://laluabdrahman.com/* https://www.laluabdrahman.com/* API restrictions: Pilih Restrict key Centang hanya "PageSpeed Insights API" Klik Save STEP 2: Deploy Cloudflare Worker2.1 Buat Worker BaruLogin ke Cloudflare dashboard Sidebar kiri: Workers & Pages Klik Create application → Create Worker Beri nama, misal: pagespeed-proxy Klik Deploy (dengan default code) Setelah deploy, klik Edit code 2.2 Paste Kode WorkerHapus semua default code, paste kode berikut: export default { async fetch(request, env, ctx) { // CORS preflight if (request.method === 'OPTIONS') { return new Response(null, { status: 204, headers: corsHeaders() }); } if (request.method !== 'GET') { return errorResponse('Only GET allowed', 405); } const url = new URL(request.url); const targetUrl = url.searchParams.get('url'); const strategy = (url.searchParams.get('strategy') || 'mobile').toLowerCase(); if (!targetUrl) { return errorResponse('Missing url parameter', 400); } if (!['mobile', 'desktop'].includes(strategy)) { return errorResponse('strategy must be mobile or desktop', 400); } // Validate target URL let target; try { target = new URL(targetUrl); } catch (e) { return errorResponse('Invalid URL format', 400); } if (!['http:', 'https:'].includes(target.protocol)) { return errorResponse('Only http(s) protocol allowed', 403); } // SSRF prevention const hostname = target.hostname.toLowerCase(); if (['localhost', '127.0.0.1', '0.0.0.0', '::1'].includes(hostname) || /^10\./.test(hostname) || /^172\.(1[6-9]|2\d|3[01])\./.test(hostname) || /^192\.168\./.test(hostname) || /^169\.254\./.test(hostname)) { return errorResponse('Hostname not allowed', 403); } if (!env.GOOGLE_API_KEY) { return errorResponse('Worker not configured: missing GOOGLE_API_KEY secret', 500); } // Cache check const cacheKey = new Request( `https://psi-cache.internal/${strategy}/${encodeURIComponent(target.href)}`, { method: 'GET' } ); const cache = caches.default; let cached = await cache.match(cacheKey); if (cached) { const newHeaders = new Headers(cached.headers); newHeaders.set('X-PSI-Cache', 'HIT'); return new Response(cached.body, { status: cached.status, headers: newHeaders }); } // Call Google PageSpeed API const apiUrl = new URL('https://www.googleapis.com/pagespeedonline/v5/runPagespeed'); apiUrl.searchParams.set('url', target.href); apiUrl.searchParams.set('strategy', strategy); ['performance', 'accessibility', 'best-practices', 'seo'].forEach(cat => { apiUrl.searchParams.append('category', cat); }); apiUrl.searchParams.set('key', env.GOOGLE_API_KEY); try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 60000); // GANTI Referer di bawah dengan domain blog Anda const apiRes = await fetch(apiUrl.toString(), { headers: { 'Referer': 'https://www.laluabdrahman.com/' }, signal: controller.signal }); clearTimeout(timeoutId); if (!apiRes.ok) { let errMsg = `Google API returned HTTP ${apiRes.status}`; try { const errBody = await apiRes.json(); if (errBody.error?.message) errMsg = errBody.error.message; } catch (e) {} return errorResponse(errMsg, apiRes.status === 429 ? 429 : 502); } const data = await apiRes.json(); const response = new Response(JSON.stringify(data), { status: 200, headers: { ...corsHeaders(), 'Content-Type': 'application/json', 'Cache-Control': 'public, max-age=3600', 'X-PSI-Cache': 'MISS' } }); ctx.waitUntil(cache.put(cacheKey, response.clone())); return response; } catch (err) { if (err.name === 'AbortError') { return errorResponse('PageSpeed API timeout (>60s)', 504); } return errorResponse('Fetch failed: ' + err.message, 502); } } }; function corsHeaders() { return { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type' }; } function errorResponse(message, status) { return new Response(JSON.stringify({ error: message }), { status, headers: { ...corsHeaders(), 'Content-Type': 'application/json' } }); } Klik Save and deploy. 2.3 Tambah API Key sebagai SecretSekarang masukkan API key Anda ke Worker sebagai secret (encrypted): Di Worker → tab Settings Scroll ke section Variables and Secrets Klik + Add Isi: Type: Secret (penting! bukan Text) Variable name: GOOGLE_API_KEY (case-sensitive, persis seperti ini) Value: paste API key dari Step 1.2 Klik Save 2.4 Test WorkerBuka di browser: https://pagespeed-proxy.YOUR-NAME.workers.dev/?url=https://example.com&strategy=mobile Ganti YOUR-NAME dengan subdomain Worker Anda. Expected: Tunggu 15-40 detik Lihat JSON besar muncul dengan field lighthouseResult Jika ada error, baca pesan dan cek troubleshooting di bawah Catat URL Worker Anda — akan dipakai di Step 3. STEP 3: Setup Tool HTML di Blogger3.1 Edit Konfigurasi ToolBuka file pagespeed-tester.html Anda. Cari baris ini (sekitar baris 510): const WORKER_URL = ''; Ganti dengan URL Worker Anda: const WORKER_URL = 'https://pagespeed-proxy.YOUR-NAME.workers.dev/'; 3.2 Paste ke BloggerBlogger dashboard → Pages → New Page Klik mode HTML view (bukan Compose) Paste seluruh isi file pagespeed-tester.html Beri title halaman: "PageSpeed Tester" Publish 3.3 Test ToolBuka halaman yang baru dipublish, lalu: Input URL, contoh: https://example.com Pilih Mobile atau Desktop Klik Mulai Tes Tunggu 15-40 detik Hasil muncul dengan score rings, Core Web Vitals, dan rekomendasi Cache test: Klik Mulai Tes lagi dengan URL dan strategy sama Harusnya muncul <1 detik dari cache Footer berubah jadi: "✓ dari cache" TroubleshootingError: "Worker not configured: missing GOOGLE_API_KEY secret"Secret belum tersimpan atau Worker belum redeploy. Solusi: Cek di Settings → Variables and Secrets, pastikan ada GOOGLE_API_KEY (case-sensitive) Pastikan Type-nya Secret, bukan Text Edit kode Worker, save lagi untuk trigger redeploy Error: "Requests from referer <empty> are blocked"API key Anda di-restrict ke domain, tapi Worker tidak kirim Referer header. Solusi: Pastikan di kode Worker ada baris ini di dalam fetch() ke Google API: headers: { 'Referer': 'https://www.YOUR-DOMAIN.com/' } Domain di sini harus match dengan salah satu restriction di Google Cloud Console. Tombol "Mulai Tes" tidak responBuka Developer Console (F12 → Console), lihat error apa yang muncul. Cek juga: WORKER_URL sudah diisi dengan URL Worker Anda URL Worker bisa diakses langsung di browser Error: "HTTP 429" (Too Many Requests)Rate limit dari Google. Tunggu 1-2 menit, lalu coba lagi. Atau: Pastikan API key sudah terkonfigurasi dengan benar Caching seharusnya mengurangi request rate Hasil PageSpeed berbeda dari pagespeed.web.devNormal. Score PageSpeed bisa fluktuasi 5-10 point antar test karena: Variasi network condition di server Google Random sampling dari data CrUX (Chrome User Experience Report) Cache TTL yang berbeda Untuk benchmark akurat, jalankan test 3-5 kali dan ambil median. Quota dan BiayaGoogle PageSpeed Insights API25,000 query per hari gratis Cukup untuk blog normal — bahkan dengan caching off Tidak perlu billing setup Cloudflare Workers100,000 request per hari gratis (free tier) 10ms CPU time per request (kita pakai jauh lebih sedikit) Cache pakai Cloudflare Cache API (built-in, gratis) Estimasi penggunaan blog dengan 1,000 visitor/hariAsumsi: 1% visitor pakai tool = 10 test/hari Resource Limit Penggunaan Cloudflare Workers 100k/hari 10 req Google API 25k/hari 10 req Cache hit rate - ~30% (URL repeat) Penggunaan masih jauh di bawah free tier. Aman untuk blog dengan trafik wajar. Cara Kerja DetailArsitektur Defense in DepthTool ini punya 5 layer keamanan: Key disembunyikan di Worker secret — encrypted di Cloudflare, tidak visible di dashboard setelah disimpan API restriction di Google — key hanya bisa pakai PageSpeed Insights API, bukan Maps/Translate yang berbayar Website restriction di Google — referer harus match domain Anda Worker forward referer — memungkinkan website restriction bekerja meski request datang dari Cloudflare IP SSRF prevention di Worker — block private IP dan localhost di parameter URL Kalau salah satu layer compromised, layer lain masih protect: Key bocor → masih di-restrict ke PageSpeed Insights API only Restriction lemah → key tetap tidak visible di browser Cache StrategyWorker pakai Cloudflare Cache API (built-in, no setup): Cache key: URL + strategy TTL: 1 jam (3600 detik) Storage: Cloudflare edge, automatic eviction Mobile dan Desktop terhitung cache terpisah. Cache di-share antar visitor — jadi kalau 100 orang test URL sama, hanya 1 yang fresh fetch, sisanya dari cache. Kenapa Hasil Pertama Lambat?Google PageSpeed API actually menjalankan Lighthouse audit di server mereka — load page, render, simulate user interaction, analyze. Itu butuh 15-40 detik. Tidak bisa diakselerasi. Setelah cache, hasil instan karena tidak perlu re-audit. KesimpulanAnda sekarang punya tool PageSpeed Tester sendiri yang: Gratis sampai 25k test/hari (jauh di atas kebutuhan blog normal) Aman — API key tidak pernah terlihat publik Cepat untuk repeat test (cache 1 jam) Customizable — bisa modify UI, tambah fitur, dll Tool ini juga jadi foundation pattern untuk tool lain yang butuh API key. Misalnya kalau Anda mau buat: Image Alt Text Generator (pakai OpenAI/Claude API) SERP Checker (pakai SerpApi) Backlink Checker (pakai Ahrefs API) Polanya sama: Worker proxy + secret env variable. Selamat mencoba!
-
Review Hawk Host
Secara keseluruhan, Hawk Host adalah pilihan yang sangat seimbang bagi blogger, pengusaha digital, dan agensi kecil yang mencari performa tinggi dengan harga yang tetap masuk akal untuk jangka panjang. Apakah Anda tertarik menyewa hosting di Hawk Host? Apakah ada yang ditanyakan sebelum membeli? Jangan sungkan bertanya. Kalau Anda punya pengalaman dengan Hawk Host bagikan di sini mungkin berguna untuk anggota lainnya. View full hosting review
-
Review Hawk Host
Hawk Host adalah penyedia layanan hosting global asal Kanada yang telah beroperasi sejak tahun 2004. Awalnya dikenal dengan nama Devoted Host, perusahaan ini kini bermarkas di Toronto dan telah berkembang menjadi pemain industri yang stabil dengan mengelola infrastruktur di berbagai benua. Hawk Host menonjol karena komitmennya pada solusi mandiri (self-sustaining), di mana sistem dirancang untuk memulihkan diri secara otomatis dari masalah teknis umum. Saya sudah berlangganan layanan mereka selama bertahun-tahun. Infrastruktur Jaringan dan Lokasi Pusat DataSalah satu kekuatan utama Hawk Host adalah jaringan pusat data Tier III dan Tier IV yang tersebar di tujuh lokasi strategis di seluruh dunia. Lokasi-lokasi tersebut meliputi: Amerika Utara: Dallas, Los Angeles, New York City, dan Toronto. Eropa: London. Asia: Singapura dan Hong Kong. Penempatan ini memungkinkan pengguna memilih lokasi yang paling dekat dengan audiens mereka untuk mendapatkan latensi rendah dan kecepatan akses maksimal. Khusus untuk pasar Asia Tenggara, keberadaan node di Singapura dan Hong Kong memberikan keuntungan latensi yang signifikan. Teknologi Server dan Optimasi PerformaHawk Host menggunakan tumpukan teknologi modern untuk memastikan performa tinggi bagi penggunanya: LiteSpeed Web Server: Digunakan pada paket shared, semi-dedicated, dan reseller, LiteSpeed diklaim hingga 900% lebih cepat dibandingkan Apache tradisional untuk konten statis dan 50% lebih cepat untuk pemrosesan PHP. Penyimpanan NVMe: Hawk Host telah mengadopsi teknologi NVMe (Non-Volatile Memory Express) di seluruh lini produk terbarunya, yang menawarkan kecepatan transfer data jauh melampaui SSD standar. Fitur Caching Gratis: Semua paket sudah termasuk server Memcached dan Redis tanpa biaya tambahan untuk mempercepat backend situs web, terutama pada platform seperti WordPress dan e-commerce. Isolasi CloudLinux dan CageFS: Teknologi ini memastikan setiap akun diisolasi dalam lingkungan virtualnya sendiri, sehingga penggunaan sumber daya oleh satu pengguna tidak akan mengganggu pengguna lain di server yang sama. Ekosistem Produk dan LayananHawk Host menawarkan berbagai kategori produk untuk memenuhi kebutuhan dari skala blog pribadi hingga aplikasi besar: Cloud Web Hosting (Shared): Produk inti yang ekonomis namun kuat, mulai dari paket Starter (15 GB NVMe) hingga Max (130 GB NVMe). Semi-Dedicated Hosting: Dirancang sebagai jembatan bagi pengguna yang butuh sumber daya lebih besar daripada shared hosting namun tidak ingin kerumitan mengelola VPS. Paket terbaru (Raptor, Stratos, Apex) menawarkan hingga 8 core CPU dan 10GB RAM. Reseller Hosting: Solusi white-label bagi agensi yang ingin menjual kembali layanan hosting dengan merek sendiri, lengkap dengan lisensi WHMCS pada paket tertentu. Cloud Compute (Cloud VPS): Memberikan akses root penuh dan virtualisasi KVM yang skalabel bagi pengembang. Keamanan dan Tata Kelola DataHawk Host mengintegrasikan berbagai lapisan perlindungan proaktif, termasuk Imunify360 yang mencakup WAF, pemindaian malware otomatis, dan pertahanan intrusi secara real-time. Selain itu, tersedia backup harian gratis yang disimpan di lokasi offsite aman untuk memudahkan pemulihan data jika terjadi kegagalan sistem. Analisis Ekonomi dan HargaHawk Host dikenal dengan strategi harga yang kompetitif dan transparan, menurut saya sangat terjangkau. Harga Pendaftaran: Seringkali dimulai di bawah $3.00 per bulan dengan promo diskon pendaftaran untuk kontrak jangka panjang. Transparansi Perpanjangan: Meskipun harga perpanjangan lebih tinggi dari harga promo awal, peningkatannya dianggap masih wajar dibandingkan penyedia hosting besar lainnya. Garansi Uang Kembali: Tersedia 30-day money-back guarantee bagi pengguna shared hosting yang kurang puas. Pendaftaran Domain: Bertindak sebagai registrar untuk berbagai ekstensi TLD dengan harga bersaing, seperti .com di harga promo sekitar $9.99. Catatan: Sekarang Anda bisa bayar via QRIS, ini sangat memudahkan bagi mereka yang tidak punya kartu kredit. Batas Teknis (Resource Limits)Hawk Host sangat transparan mengenai batas sumber daya (LVE Limits) untuk setiap paketnya: Concurrent PHP Processes: Paket shared hosting dibatasi pada 10 hingga 15 koneksi PHP simultan. Ini merujuk pada proses aktif pada satu instan waktu, bukan jumlah total pengunjung, sehingga dengan caching yang baik, situs tetap bisa melayani ribuan pengunjung per jam. Inodes: Batas inode cukup murah hati, mulai dari 500.000 pada paket dasar hingga 5.000.000 pada paket tingkat atas. Dukungan PelangganDukungan teknis tersedia 24/7/365 melalui sistem tiket, live chat, dan dokumentasi basis pengetahuan (knowledge base) yang luas. Komunikasi dalam bahasa Inggris, tidak tersedia bahasa Inonesia. Tim dukungan sangat kompeten dalam menangani masalah teknis, terutama terkait manajemen server dan WordPress. Hawk Host juga menyediakan layanan migrasi gratis bagi pengguna baru yang ingin pindah dari penyedia hosting lama.
-
Tutorial Komprehensif Membuat Homepage Kustom di Invision Community 5
Panduan lengkap membuat homepage di Invision Community 5 agar tampak berbeda dari tampilan bawaan. Minimalis dan modern. Menyertakan block live yang menampilkan statistik artikel, forum, jumlah anggota aktif. View full article
-
Tutorial Komprehensif Membuat Homepage Kustom di Invision Community 5
Panduan lengkap berdasarkan pengalaman membangun lalu.pro — dengan Invision Community 5.0.18. Target: Pengguna IPS 5 yang ingin membangun homepage editorial dengan data live dari berbagai aplikasi IPS (Forum, Pages, Downloads). Tingkat: Menengah (butuh dasar HTML/CSS, tidak butuh PHP advance). PrasyaratSebelum mulai, pastikan Anda punya: Sebelum mulai, pastikan Anda punya: Teknis Invision Community 5.0.x ter-install (panduan ini di-test di 5.0.18) Aplikasi Pages aktif (wajib — ini yang memungkinkan custom homepage) AdminCP access sebagai administrator Akses cPanel atau file manager (untuk backup tema, opsional tapi sangat direkomendasikan) Pengetahuan HTML dasar (tag, attribute, nested element) CSS dasar (selector, properties, flexbox, grid) Konsep template tag (mirip Twig, Liquid, atau Blade — tidak perlu PHP) Mengerti perbedaan client-side (HTML/CSS) vs server-side (PHP) rendering Aplikasi IPS yang Direkomendasikan untuk Live Data Forum (untuk fetch topik diskusi) Pages dengan custom database (untuk artikel) Downloads (untuk file/resources) Calendar (untuk events — opsional) Persiapan Konten Minimal 3-5 topik forum sudah dipost Minimal 3-5 artikel sudah di-publish di Pages database Minimal 2-3 file sudah di-upload di Downloads Tanpa konten ini, section live data akan tampak kosong saat testing. Konsep IPS 5 yang Wajib DipahamiSebelum nyentuh kode, pahami dulu konsep-konsep ini. Banyak frustrasi muncul karena pengguna tidak tahu vocabulary IPS 5. 1. Pages — Aplikasi Pembuat HalamanPages adalah aplikasi IPS yang memungkinkan Anda membuat halaman custom di luar struktur forum standar. Homepage custom Anda akan jadi sebuah Page. Path: AdminCP → Pages 2. Page TypesSaat membuat Page baru, ada dua tipe utama: Tipe Kapan Dipakai Tutorial Ini Pakai? Page Builder Visual drag-and-drop dengan widget Tidak Manual HTML/template tag custom penuh Ya Manual memberi Anda kontrol penuh atas HTML, sementara Page Builder cocok untuk yang tidak mau repot ngoding. 3. Use Suite HTML Wrapper — Setting Paling KrusialIni setting yang sering bikin bingung. Ada dua mode: Mode A: Suite Wrapper ON (Direkomendasikan untuk Production)✅ Konten Anda di-wrap dalam template global IPS (header, footer, sidebar, scripts) ✅ Security IPS aktif (CSRF, session, auth) ✅ JavaScript dan PHP IPS jalan native ✅ Konsisten dengan halaman lain (forum, artikel detail) ❌ Tidak bisa custom <head>, <body>, atau full HTML Kapan pakai: Saat Anda mau pakai header & footer bawaan IPS, dan hanya custom area konten tengah. Mode B: Suite Wrapper OFF (Full Custom)✅ Full kontrol dari <!DOCTYPE html> sampai </html> ✅ Bisa pakai font, CSS, JS dari mana saja ❌ Harus handle security sendiri ❌ Tidak otomatis dapat update template IPS ❌ Header/footer/user dropdown IPS hilang ❌ Login/logout/notifications bawaan IPS tidak otomatis ada Kapan pakai: Saat homepage benar-benar berbeda total dari sisa situs (misal, landing page marketing). 4. Blocks — Mesin Live DataBlocks adalah komponen reusable yang bisa menampilkan data dari aplikasi IPS. Ada 4 tipe block di IPS 5: Tipe Fungsi Contoh Penggunaan Plugin Feed otomatis dari aplikasi IPS Topic Feed, Record Feed, File Feed Manual HTML HTML + expression tag + PHP inline Counter, custom widget Editor Konten statis pakai WYSIWYG editor Welcome message, announcement Template Reusable dengan input fields Multi-purpose container Tutorial ini akan beberapa pakai Plugin (untuk feed) dan Manual HTML (untuk stats counter). 5. Block TemplatesTampilan default block IPS sering tidak match dengan desain Anda. Solusinya: Block Template — custom template yang bisa Anda terapkan ke block tertentu. Path: AdminCP → Pages → Templates → Add → block template Block Template menggunakan template tag IPS ({{foreach}}, {$variable}, dll) — bukan PHP murni. 6. Template Tags vs ExpressionDua jenis sintaks yang akan sering Anda lihat: Template Tag (logic flow): {{if $count > 1000}} {{foreach $items as $item}} ... {{endforeach}} {{endif}}Expression (eksekusi PHP): {expression="number_format($count)"} {expression="$record->_title"}Aturan: gunakan {{...}} untuk logic, {expression="..."} untuk output PHP. 7. Default Page SettingSetelah membuat homepage, Anda harus set sebagai default agar muncul saat user buka root domain (https://situs.com/). Path: AdminCP → System → Settings → Site Features → Default Page Fase 1 — Setup Halaman HomepageStep 1.1: Backup Tema Anda (Wajib!)Sebelum modifikasi apapun, backup tema aktif: AdminCP → Customization → Appearance → Themes └─ Hover icon download di samping tema → Save .xml file Simpan file .xml ini di komputer lokal. Kalau ada yang rusak, Anda bisa restore. Step 1.2: Buat Page BaruPath: AdminCP → Pages → Add Page Pilih Content Editor-nya Manual HTML Lengkapi Page Details dengan isian seperti dalam tabel di bawah. Isian mencakup: Field Isi dengan Catatan Page Name Homepage Display name internal Page Filename home URL slug (tanpa ekstensi), jika dikosongkan otomatis namanya mengikuti isian Page Name Page Folder Select Biarkan kosong untuk root, centang No Parent Use suite HTML wrapper ON (centang) Critical setting Page Title Komunitas Blogger Indonesia Untuk SEO <title> Content Kode HTML Lihat step 1.3 Meta Description (deskripsi singkat) Untuk SEO Klik Save & Edit. Step 1.3: Struktur HTML DasarKarena Suite Wrapper ON, TIDAK butuh <!DOCTYPE>, <html>, <head>, atau <body>. IPS akan menambahkannya otomatis. Anda cukup tulis konten yang ada di dalam <body> saja. Template dasar yang bisa Anda pakai sebagai starting point: <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@300;400;500;600;700&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet"> <style> /* Reset & base */ * { margin: 0; padding: 0; box-sizing: border-box; } :root { --bg: #ebe7e0; --ink: #181818; --accent: #ff5c2b; /* ... variabel lainnya ... */ } /* Style section di sini */ </style> <!-- HERO SECTION --> <section class="hero"> <h1>Selamat datang di komunitas kami</h1> <p>Deskripsi singkat tentang komunitas Anda.</p> </section> <!-- SECTION LAIN --> <section class="features"> <!-- ... --> </section>Itu saja kerangka dasarnya. Step 1.4: Penting — Cara Paste Kode di Editor IPSEditor IPS Pages punya mode Invision HTML Template dan Plain Text. Klik tab Content, secara default terpilih editor Invision HTML Template. Salin kerangka HTML pada langkah 1.3 di atas. Paste di kolom yang tersedia. Save. Setelah Save tentukan View Page, centang saja semua agar homepage dapat dilihat oleh semua pengunjung. Step 1.5: Set Homepage Sebagai DefaultSetelah halaman terbuat, jadikan default, langsung saja klik ikon bintang, lalu geser tombol Set as default page ke kanan (ON). Klik Save. Sekarang akses https://situs-anda.com/ — homepage Anda muncul. Catatan: Jangan lupa atur aplikasi Pages menjadi default juga. Step 1.6: VerifikasiBuka homepage di browser baru (incognito) untuk pastikan tampil benar: Tampil tanpa error Header & footer IPS muncul (karena Suite Wrapper ON) Konten custom Anda di tengah Font Google Fonts loaded (cek dengan Inspector → Network) Sampai di sini Anda sudah berhasil membuat homepage. Tapi masih sangat sederhana, hanya berisi teks selamat datang. Selanjutnya kita akan menambahkan beberapa block live. Fase 2 — Live Data IntegrationBagian ini yang membuat homepage Anda hidup — menampilkan data real dari forum, artikel, dan files. 2.1 Strategi Konversi BertahapUrutan pekerjaan saya saat membuat homepage lalu.pro adalah per section. Urutan Section Tipe Block Tingkat Kesulitan 1 Stats counter (member, topic, artikel) Manual HTML Mudah 2 Latest articles Plugin (Record Feed) Sedang 3 Hot topics Plugin (Topic Feed) Sedang 4 Latest files (Download) Plugin (File Feed) Sedang Tapi saya hanya akan memandu Anda membuat stat counter dulu agar Anda melihat tampilannya dan memahmi alur kerjanya. 2.2 Stats Counter — Member CountKita perlu membuat sebuah block. Path: AdminCP → Pages → Blocks → Create New Block → pilih "Manual HTML Setelah klik tombol Next, isi form Details dan Content. Title: Stat Members Count Template Key: lalu_stat_members, (tanpa spasi) Description: (boleh kosong) Content (paste persis): {expression="number_format( \IPS\Db::i()->select( 'COUNT(*)', 'core_members', array( 'completed=?', 1 ) )->first() )"}Penjelasan kode: \IPS\Db::i() — akses database IPS select('COUNT(*)', 'core_members'...) — hitung jumlah member 'completed=?', 1 — hanya yang sudah verifikasi email number_format(...) — format dengan koma (2,847) Klik Save. Klik Save sekali lagi (set view page). 2.3 Stats Counter — Topic CountSekarang buat block baru lagi, langkah-langkahnya seperti block member counter. Isiannya: Title: Stat Topics Count Template Key: lalu_stat_topics Content: {{$count = \IPS\Db::i()->select( 'COUNT(*)', 'forums_topics', array( 'approved=?', 1 ) )->first();}} {{if $count >= 1000}} {expression="round( $count / 1000, 1 )"}<em>k</em> {{else}} {expression="number_format( $count )"} {{endif}}Penjelasan: Hitung topic yang approved (approved=1) Kalau ≥1000, tampilkan format 14k, 1.5k Kalau <1000, tampilkan angka penuh 2.4 Stats Counter — Article CountCek dulu Database ID Articles: AdminCP → Pages → Databases └─ Cari "Articles" → catat angka ID di kolom paling kiri Misal ID = 1, maka tabel-nya adalah cms_custom_database_1. Buat block dengan isian berikut: Title: Stat Articles Count Template Key: lalu_stat_articles Content (ganti angka 1 dengan ID Anda): {expression="number_format( \IPS\Db::i()->select( 'COUNT(*)', 'cms_custom_database_1', array( 'record_approved=?', 1 ) )->first() )"}2.5 Pasang Stats Block di HomepageDi Page Content homepage, di tempat yang sesuai: <div class="hero-stats"> <div class="stat"> <div class="stat-num">{block="lalu_stat_members"}</div> <div class="stat-label">Member aktif</div> </div> <div class="stat"> <div class="stat-num">{block="lalu_stat_topics"}</div> <div class="stat-label">Diskusi</div> </div> <div class="stat"> <div class="stat-num">{block="lalu_stat_articles"}</div> <div class="stat-label">Tutorial</div> </div> </div>Anda bisa mencoba kode yang siap pakai berikut: <style> /* Reset & 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; } } </style> <!-- HERO SECTION --> <section class="hero"> <h1>Selamat datang di komunitas kami</h1> <p>Deskripsi singkat tentang komunitas Anda.</p> <div class="hero-stats"> <div class="stat"> <div class="stat-num">{block="lalu_stat_members"}</div> <div class="stat-label">Member aktif</div> </div> <div class="stat"> <div class="stat-num">{block="lalu_stat_topics"}</div> <div class="stat-label">Diskusi</div> </div> <div class="stat"> <div class="stat-num">{block="lalu_stat_articles"}</div> <div class="stat-label">Tutorial</div> </div> </div> </section>Save → Clear cache → Refresh. Lihat hasilnya, harusnya seperti berikut: Saya harap Anda memahami kerjanya dan bisa berkreasi.
-
Selamat Datang
Halo dan selamat datang di Lalu Pro. Saya mencari teman para kreator, webmaster, dan pemikir digital yang percaya bahwa di balik setiap baris kode dan taktik optimasi, ada proses belajar yang konsisten. Langkah awal anda di sini: Pahami fondasi: Sebelum memulai diskusi, kami sangat menyarankan Anda untuk membaca panduan dan aturan di sub-forum ini demi menjaga ruang diskusi tetap sehat dan produktif. Perkenalkan diri: Bagikan cerita tentang proyek web atau blog yang sedang Anda bangun di sub-forum Perkenalan. Mulai miskusi: Punya kendala server, strategi SEO yang macet, atau eksperimen monetisasi yang ingin dibagikan? Jangan ragu untuk membuat topik baru di kategori yang sesuai. Mari saling bertukar insight, menyempurnakan strategi, dan menyelesaikan pikiran-pikiran hebat bersama di sini. Salam hangat, Lalu Founder & Site Administrator