DTE :]

Wednesday, December 11, 2019

Menggunakan GitHub Sebagai Media Penyimpanan Berkas CSS dan JavaScript Eksternal

Repositori GitHub
GitHub

Kalian tidak perlu takut dengan GitHub, dan tidak perlu takut juga untuk membaca artikel ini, karena di sini Saya tidak akan membahas mengenai konsep-konsep manajemen kode perangkat lunak. Di sini Saya hanya ingin menunjukkan kepada para pemula yang sebelumnya tidak pernah menyentuh GitHub, bahwa sebenarnya terdapat begitu banyak peluang untuk menggunakan GitHub sebagai media penyimpanan berkas-berkas eksternal pada blog kalian.

Sebenarnya Saya sudah pernah membahas mengenai cara menggunakan Google Code untuk menangani kasus serupa sebelumnya, hanya saja karena sejak 25 Januari 2016 yang lalu, Google memutuskan untuk mematikan layanan Google Code mereka, maka artikel tersebut kini sudah tidak ada gunanya lagi.

GitHub, sebagaimana Google Code, pada mulanya hanyalah sebuah situs layanan penyimpanan daring yang berfungsi untuk menyimpan dan merekam segala revisi dalam kode-kode perangkat lunak sepanjang waktu yang dikombinasikan dengan fitur media sosial, untuk mempermudah para pengembang dalam berinteraksi satu sama lain. Tanpa GitHub, kita hanya bisa melakukan perekaman revisi kode melalui tampilan antarmuka baris perintah (command line interface).

Kita umpamakan saja GitHub sebagai “media sosial untuk para pengembang”, dimana anggota-anggota di dalamnya tidak mengunggah foto dan mengepos pembaruan status, melainkan mengunggah kode-kode perangkat lunak dan melakukan revisi secara rutin pada kode-kode tersebut. Yang kemudian revisi-revisi tersebut akan tampil sebagaimana pembaruan-pembaruan status yang biasa kalian lihat pada media sosial. Kalian bisa memberikan komentar, dan juga memberikan reaksi. Namun belakangan ini, GitHub lebih banyak beralih fungsi sebagai media untuk membuat web statis; menampilkan halaman-halaman web yang (biasanya) dibuat secara otomatis melalui alat pembangun seperti Jekyll, Hugo dan GatsbyJS. Ini berarti bahwa hanya dengan GitHub saja kalian sebenarnya sudah bisa menampilkan berkas-berkas web secara langsung tanpa melalui perantara. Berkas-berkas seperti HTML, CSS dan JavaScript adalah yang paling umum ada pada GitHub karena fungsi dasar GitHub yang memang untuk menyimpan kode. Namun itu bukan berarati bahwa kalian tidak bisa menyimpan berkas-berkas yang lain seperti berkas fon, musik, video dan gambar.

Membuat Repositori untuk Pertama Kali

Ini sudah tahun 2019, sebuah tahun dimana anak-anak sekolah dasar saja sudah bisa membuat akun Twitter sendiri. Saya tidak ingin bertele-tele menjelaskan tentang bagaimana cara mendaftarkan diri ke GitHub langkah demi langkah. Saya anggap kalian sudah paham itu dan Saya anggap sekarang kalian sudah punya akun GitHub dan sedang dalam kondisi log masuk.

Untuk mulai menyimpan berkas-berkas blog kalian, pertama-tama buat sebuah repositori baru dengan cara mengeklik tombol “tambah” pada pojok kanan atas, kemudian pada menu yang tampil, pilih New repository:

Repositori baru
Membuat repositori baru.

Tambahkan nama dan deskripsi bila perlu, kemudian klik Create repository:

Nama dan deskripsi repositori
Menentukan nama.

Sebuah repositori baru yang kosong akan dibuat pada URL https://github.com/nama-pengguna/blog. Untuk menambahkan berkas, klik pada tombol atau tautan yang bertuliskan Create a new file:

Buat berkas baru
Membuat berkas baru.
Membuat berkas baru
Mengisi bidang nama dan konten berkas.

Tip: Untuk membuat folder, ketik nama folder pada bidang nama berkas kemudian ketik /, maka sebuah folder akan dibuat dan bidang nama akan kosong kembali. Setelah itu kalian bisa menuliskan nama berkas yang sifatnya wajib.

Setelah berkas selesai dibuat, klik tombol Commit new file. Sebuah berkas baru bernama layout.css akan tampil pada daftar, namun berkas ini masih bersifat mentah dan tidak bisa diakses secara langsung. Kalaupun memang bisa (dengan mencari tombol Raw pada berkas terkait), tipe MIME yang diberikan nanti adalah berupa text/plain sehingga berkas tersebut tidak akan bisa digunakan sebagai berkas CSS karena peramban menganggap berkas tersebut sebagai berkas teks biasa. Untuk mengatasi masalah tersebut, kalian perlu mengaktifkan fitur GitHub Pages.

Mengaktifkan Fitur Halaman GitHub

Untuk mengaktifkan fitur halaman GitHub, klik tab Settings kemudian temukan set bidang yang memiliki judul GitHub Pages. Pada bidang Source, ubah nilainya dari none menjadi master:

Pengaturan fitur halaman GitHub
Mengaktifkan fitur halaman GitHub.

Fitur halaman sudah aktif. Sekarang kalian bisa mengakses akar repositori sebelumnya secara langsung melalui URL https://nama-pengguna.github.io/blog. Dan karena berkas layout.css berada pada akar halaman, maka kalian bisa mengaksesnya melalui URL https://nama-pengguna.github.io/blog/layout.css. Seperti ini hasilnya:

Tembolok GitHub
Melihat tipe MIME melalui tab Network pada alat pengembang.

Kalian bisa memuat URL tersebut pada elemen <link> dan <script> di dalam kode HTML tema seperti ini:

<!DOCTYPE html>
<html dir="ltr">
  <head>
    <meta charset="utf-8">
    <title> … </title>
    <link href="https://nama-pengguna.github.io/blog/layout.css" rel="stylesheet">
  </head>
  <body>
    …
    …
    <script src="https://nama-pengguna.github.io/blog/layout.js"></script>
  </body>
</html>

Memaksimalkan Performa

Saya tidak begitu yakin bahwa berkas yang diakses secara langsung melalui URL halaman GitHub akan disimpan sebagai tembolok dalam jangka waktu yang lama karena GitHub sangat kerap dengan aktivitas pembaruan kode. Jangka waktu tembolok yang lama agaknya tidak akan menguntungkan dari sisi pengguna. Akan selalu ada kemungkinan para pengguna mengalami kebingungan karena berkas yang sudah diperbarui ternyata tidak mengalami perubahan juga ketika dimuat ulang (kecuali dengan memuatnya secara paksa menggunakan kombinasi kunci Ctrl + R pada papan ketik). Untuk berkas-berkas yang biasa dimuat secara langsung seperti berkas HTML dan XML mungkin masih terbilang mudah untuk dipaksa, tapi bagaimana dengan berkas-berkas yang dimuat di dalam kode HTML seperti berkas CSS dan JavaScript? Hal ini yang membuat keputusan untuk memberikan jangka waktu tembolok yang pendek pada GitHub menjadi tampak lebih bijaksana.

jangka waktu tembolok
Bandingkan waktu pada bidang Last-Fetched dengan waktu pada bidang Expires.

Akan tetapi, untuk memuat berkas CSS dan JavaScript pada situs web versi produksi dengan jangka waktu tembolok yang pendek tentu akan mengurangi performa situs web karena setiap kali halaman diakses, berkas-berkas CSS dan JavaScript yang ada akan selalu dianggap baru oleh peramban dan oleh karena itu mereka akan selalu dimuat melalui peladen GitHub dan tidak pernah bisa dimuat melalui tembolok yang telah tersimpan di peramban pada komputer masing-masing pengguna.

Ada sebuah situs web layanan pihak ke tiga yang sangat sesuai untuk menangani masalah ini yaitu Statically. Untuk menggunakannya, cukup tempelkan URL berkas versi mentah GitHub pada bidang URL repositori yang ada dan kemudian gunakan URL versi produksinya pada halaman web kalian:

Statically
Statically

Pada URL versi produksi tersebut selalu terdapat commit hash yang dapat digunakan sebagai penanda versi sebagaimana ketika kalian menambahkan parameter ?v=1.x.x di belakang URL berkas CSS dan JavaScript untuk memperbarui tembolok secara paksa. Setiap kali berkas direvisi, maka hash baru akan dibuat. Pastikan untuk menempelkan kembali URL berkas versi mentah yang baru pada bidang URL repositori untuk mendapatkan versi berkas yang sudah diperbarui. Setiap berkas yang dimuat melalui URL tersebut akan disimpan sebagai tembolok selama setahun:

Tembolok CDN Statically
Melihat tipe MIME melalui tab Network pada alat pengembang.

Yang Harus Diperhatikan

  • Untuk menghindari hal-hal yang tidak diinginkan seperti pemblokiran akun oleh pihak GitHub, pastikan untuk menggunakan GitHub sebagai media penyimpanan berkas-berkas yang bersifat terbuka. Versi minified masih bisa diterima, versi packed dan obfuscated mungkin akan cenderung dicurigai dan oleh karena itu akan memiliki potensi yang lebih besar untuk diblokir.
  • Meskipun bisa, Saya tetap tidak menyarankan kalian untuk menyimpan berkas-berkas dalam bentuk video dan musik karena sampai sekarang Saya masih belum pernah melihat berkas-berkas berjenis video dan musik di GitHub.
  • Kalau kalian ingin menyimpan berkas fon untuk dimuat pada halaman web, pastikan dulu lisensi fon yang ada apakah memang boleh untuk digunakan pada halaman web secara legal. Beberapa fon, meskipun diketahui bersifat gratis namun ada juga yang tidak boleh dimuat sebagai fon untuk web.

Cara yang paling mudah untuk mengakali kebijakan dan layanan GitHub untuk privasi semacam ini adalah dengan menge-fork repositori publik milik orang lain (atau kalian juga bisa menge-fork repositori khusus Blogger milik Saya) dan kemudian menambahkan berkas-berkas pribadi kalian ke dalam repositori tersebut. Meskipun berkas-berkas pribadi kalian tidak ditujukan untuk komunitas kode sumber terbuka, setidaknya beberapa berkas yang lain dari hasil fork tersebut akan membuat repositori kalian seolah-olah adalah repositori yang “ramah” seperti repositori-repositori pada umumnya. Risiko ditanggung sendiri.

Labels: ,

Wednesday, June 19, 2019

Manajemen Anggaran Web Hosting untuk Menekan Biaya Sewa

Web Hosting Plan
Image by Freepik.

Ketika Saya masih duduk di bangku SMP dan masih menggunakan mesin Blogger untuk membuat situs web, Saya tidak tahu apa itu sistem manajemen konten dan apa itu domain. Saya juga tidak tahu kenapa Saya harus membayar semua itu dan bagaimana mekanisme pembayarannya. Dan pada saat yang sama Saya mempunyai beberapa orang kenalan yang sudah bisa membuat situs web pribadi dengan cara self-host. Yang berarti bahwa mereka mengunduh paket sistem manajemen konten dari internet, menyewa ruang penyimpanan dan domain setiap tahun, dan kemudian mereka mengunggah sistem manajemen konten tersebut ke ruang penyimpanan mereka agar versi publiknya bisa diakses oleh semua orang: melalui domain.

Keren memang, tapi dalam jangka waktu kurang dari dua tahun saja, ketika suatu hari Saya sedang berkunjung ke blog mereka, tiba-tiba saja blog mereka sudah berubah menjadi situs web yang… entah Saya tidak tahu apa maksud dari artikel-artikel di dalamnya karena semua tulisannya menggunakan huruf Cina. Tampilan desainnya juga sudah berubah total!

Semakin ke sini tentu pengetahuan Saya akan teknologi informasi semakin bertambah, dan pada saat itu akhirnya Saya tahu bahwa yang menyebabkan blog kenalan-kenalan Saya berubah menjadi seperti itu adalah karena domain mereka sudah kadaluarsa. Domain menjadi kadaluarsa ketika pemilik domain sudah tidak mampu lagi untuk membayar biaya sewa tahunan. Karena sudah kadaluarsa maka dapat dibeli oleh orang lain. Yang berarti bahwa ketika domain tersebut sudah berpindah tangan, maka domain tersebut dapat dipergunakan oleh si pembeli yang baru itu untuk mengarahkan pengunjung-pengunjung lama dari blog kenalan Saya ke situs-situs web yang berbeda.

Kasus-kasus semacam ini sering terjadi pada situs-situs web pribadi khususnya situs-situs web berjenis blog yang berisi esai-esai opini pribadi dari si penulis blog tersebut. Situs-situs web semacam ini biasanya dibuat atas dasar suka rela tanpa mengharapkan imbalan apapun. Oleh karena itu wajar jika ada saatnya pemilik situs web menjadi tidak mampu lagi untuk membayar biaya sewa domain dan hosting, atau kalaupun masih mampu, maka pembiayaan sewa situs web seolah sudah bukan lagi menjadi prioritas. Mungkin karena bosan, atau karena mereka sudah terlalu sibuk mengurusi kepentingan-kepentingan yang lain, berpindah-pindah media sosial untuk menemukan audiens yang lebih banyak, karena masalah di dunia nyata dan lain sebagainya.

Dalam pembuatan situs web mandiri, domain (alamat akses) dan hosting (ruang penyimpanan data) merupakan dua hal yang tidak mungkin bisa dipisahkan. Situs web mandiri juga memerlukan berbagai perawatan yang harus dilakukan sendiri. Selain menulis artikel dan mengelola komentar-komentar yang masuk, satu hal yang tidak kalah pentingnya adalah pembayaran biaya sewa yang perlu dilakukan setiap tahun. Masalahnya adalah, ketika Anda tidak pandai dalam menyiasati perputaran uang dalam situs web mandiri Anda, pada akhirnya Anda akan mengalami kebangkrutan seiring berjalannya waktu. Konsekuensinya adalah situs web Anda menjadi tidak bisa lagi diakses oleh orang lain, dan domain Anda juga akan bisa dibeli oleh orang lain. Berikut ini adalah beberapa pertimbangan dan cara yang telah Saya lakukan untuk menjaga situs-situs web mandiri Saya tetap hidup di internet…

Jangan Membayar Sesuatu yang Tidak Anda Pakai

Hal pertama yang harus Anda pertimbangkan adalah mengenai perbandingan antara besar ruang penyimpanan dan biaya yang harus dikeluarkan dengan banyaknya ruang penyimpanan yang sebenarnya Anda butuhkan. Dalam situs-situs penyedia layanan web hosting biasanya terdapat pilihan-pilihan paket sewa yang dikategorikan berdasarkan fitur dan ukuran ruang penyimpanan. Pilihan pertama biasanya diadakan untuk pengguna yang ingin membuat situs web pribadi yang sederhana, sedangkan pilihan terakhir biasanya diadakan untuk digunakan oleh perusahaan-perusahaan besar yang memang bergerak dalam industri teknologi informasi dan komunikasi yang di dalamnya terdapat interaksi pengguna yang sangat aktif. Nah, sebagai seorang calon pelanggan, Anda perlu bijak dalam mengambil keputusan karena ini menyangkut pembiayaan rutin juga yang akan mempengaruhi kehidupan Anda sehari-hari. Caranya adalah dengan melakukan introspeksi diri, untuk menemukan kesimpulan mengenai apa sih sebenarnya tujuan utama Anda dalam membuat situs web.

Jika yang ingin Anda buat adalah sekedar sebuah situs web pribadi yang hanya terdiri dari halaman profil, halaman berisi formulir kontak dan pembaharuan status, maka pilihan pertama yang paling murah biasanya sudah cukup. Jika yang ingin Anda buat adalah toko daring minimalis yang bertujuan untuk berjualan dimana di dalam situs web tersebut terdapat interaksi antara pembeli dan penjual melalui keranjang belanja dan juga fitur obrolan, maka pilihan pertengahan biasanya sudah cukup untuk memenuhi kebutuhan. Sedangkan untuk pembuatan situs web yang memiliki akun pengguna dalam jumlah yang besar seperti situs web pemerintah atau forum masyarakat dimana administrator tidak memiliki kendali untuk membatasi konten apa saja yang akan pengguna terbitkan atau berkas-berkas apa saja yang akan pengguna unggah ke dalam ruang penyimpanan, maka pilihan terakhir mau tidak mau harus diambil karena pertimbangan beberapa faktor seperti tidak adanya batas kuota pembuatan akun pengguna baru, yang mana setiap satu akun baru akan meningkatkan kebutuhan ruang penyimpanan untuk satu orang pengguna.

Pastikan Anda memilih paket ruang penyimpanan sesuai dengan kebutuhan. Dengan begitu, Anda tidak harus membayar sewa ruang penyimpanan yang tidak akan pernah Anda pakai setiap tahun. Anda tidak perlu khawatir dengan lebar pita, karena secara umum ruang penyimpanan dan lebar pita akan dihitung secara terpisah oleh penyedia jasa. Gunakan sisa ruang penyimpanan yang ada semaksimal mungkin sesuai dengan perkembangan situs web Anda saat itu. Hingga ketika suatu hari Anda mendapatkan peringatan bahwa ruang penyimpanan telah mencapai batas maksimal, maka Anda bisa meminta pihak penyewa untuk melakukan upgrade ruang penyimpanan pada akun Anda, sehingga Anda bisa meningkatkan biaya sewa sesuai dengan peningkatan ruang penyimpanan yang diberikan saja.

Ketahui Ukuran Berkas Berdasarkan Jenisnya

Hasil laporan HTTP Archive dari tahun 2016 hingga 2019 menyatakan bahwa ukuran rata-rata sebuah halaman web berada pada kisaran 1896.8 KB atau sekitar 1.8 MB (hampir 2 MB). Ini sudah termasuk berkas CSS, JavaScript dan gambar yang disertakan di halaman tersebut. Anda bisa menggunakan angka ini untuk melakukan perkiraan jumlah lebar pita yang diperlukan setiap hari yang akan Saya bahas nanti. Ukuran rata-rata berkas CSS dalam satu halaman adalah 62.4 KB sedangkan ukuran rata-rata berkas JavaScript dalam satu halaman adalah 396.3 KB. Berdasarkan data ini setidaknya Anda memerlukan ruang penyimpanan sebesar 458.7 KB untuk menyimpan berkas CSS dan JavaScript. Namun, karena berkas-berkas seperti CSS dan JavaScript biasanya dimuat pada seluruh halaman sebanyak satu kali saja, maka beban berkas-berkas ini bagi ruang penyimpanan tidak akan sebesar berkas-berkas yang biasanya akan disertakan sebagai bagian dari artikel satu per satu seperti berkas gambar, musik dan video.

Saya adalah salah satu orang yang merasakan betul imbas dari berkas-berkas ini terhadap ruang penyimpanan Saya karena Saya adalah salah satu penulis blog bertema art direction yang masih bertahan sampai sekarang.

Kompres Ukuran Berkas Versi Produksi

Buat satu salinan berkas situs web sebagai versi produksi dan anggap berkas situs web yang asli sebagai berkas versi pengembangan. Lakukan optimalisasi pada semua berkas versi produksi untuk mengurangi ukuran berkas semaksimal mungkin.

Gunakan aplikasi pihak ke tiga untuk melakukan kompresi berkas-berkas yang akan dimuat pada situs web versi produksi Anda. Misalnya, Anda bisa menggunakan UglifyJS untuk mengompres berkas JavaScript dan UglifyCSS untuk mengompres berkas CSS. Untuk mengompres ukuran gambar, saat ini Saya merekomendasikan TinyJPG dan TinyPNG yang telah terbukti mampu menurunkan ukuran berkas hingga > 70% pada gambar-gambar PNG dan JPEG Saya tanpa mengurangi kualitas tampilan gambar. Dan jika memang diperlukan, Anda juga bisa mengompres berkas HTML dengan memanfaatkan plugin tertentu pada sistem manajemen konten yang Anda pakai. Optimalisasi sumber daya seperti ini tidak hanya akan mengurangi jumlah beban yang harus ditanggung oleh kuota penyimpanan yang disediakan oleh layanan web hosting Anda tetapi juga dapat mempercepat proses muat halaman dari sisi klien, sehingga akan meningkatkan pengalaman pengunjung terhadap situs web Anda.

Manfaatkan Layanan CDN Publik

Pada tahun 2010, Google meluncurkan perpustakaan fon dengan lisensi bebas pakai yang dapat dipakai oleh semua orang di berbagai penjuru dunia setelah era Web Safe Font dan Cufón berakhir seiring dengan meningkatnya permintaan dan dukungan fitur @font-face pada setiap peramban mayor. Layanan tersebut termasuk dalam kategori layanan CDN publik, karena setiap orang bebas untuk memuat berkas-berkas fon dari Google pada situs web mereka melalui tautan yang disediakan. Tanpa disadari, sebelumnya kita juga telah mengenal CDN yang berfungsi untuk memuat skrip-skrip populer seperti jQuery, MooTools dan Dojo melalui Google API. Sejak saat itu, muncul berbagai layanan CDN sejenis (hampir semuanya gratis) yang bertujuan untuk mempermudah pengguna di seluruh dunia dalam memuat berkas-berkas sejenis tanpa harus memiliki peladen web terdedikasi dengan cara memusatkan berkas-berkas populer di seluruh dunia pada satu peladen yang sama.

Meskipun tujuan utama layanan-layanan CDN semacam ini sama sekali tidak ada hubungannya dengan solusi penyusutan ruang penyimpanan pada peladen web masing-masing, namun dengan menghapus beberapa berkas dari ruang penyimpanan yang ternyata sudah tersedia di berbagai CDN publik tentu dapat menghemat ruang penyimpanan beberapa bita serta dapat membebaskan Anda dari beban lebar pita pada berkas-berkas tersebut.

Pilih Sistem Manajemen Konten Sesuai Kebutuhan

Hampir semua layanan web hosting dilengkapi dengan Softaculous Apps Installer yang menyediakan berbagai pilihan untuk membantu Anda membuat situs web hanya dengan sekali klik. Karena adanya fitur tersebut, yang biasanya juga akan menampilkan daftar sistem manajemen konten yang diurutkan berdasarkan jumlah instalasi yang telah dilakukan oleh pengguna sepanjang waktu, maka tidak heran apabila sistem manajemen konten seperti WordPress, Joomla!, Drupal dan PrestaShop menjadi begitu populer bahkan bagi kalangan pengguna yang tidak mengerti tentang bahasa pemrograman web sama sekali.

Tapi secara garis besar, penentuan pilihan dapat dipertimbangkan berdasarkan jenis situs web apa yang akan dibuat dan seberapa luas situs web tersebut dapat berkembang. Sebagai contoh, apabila Anda ingin membuat situs web dengan tujuan untuk mempublikasikan berita-berita terkini dimana dalam situs web tersebut terdapat beberapa orang penulis yang saling berkolaborasi menjaga dan mengelola segala aktivitas yang ada, maka WordPress dapat menjadi pilihan yang tepat.

Apabila Anda ingin membuat situs web berjenis forum diskusi atau situs web yang bertujuan untuk mengumpulkan banyak orang dengan fitur yang sangat kompleks di mana suatu hari nanti mungkin situs web tersebut dapat berkembang menjadi lebih luas lagi, dengan penambahan fitur-fitur seperti blog dan toko, formulir registrasi identitas masyarakat, situs web berganda dengan manajemen terpusat, dan lain sebagainya dan lain sebagainya, maka Drupal dan Joomla! dapat menjadi pilihan yang tepat. Akan tetapi menjadi terlalu berlebihan apabila tujuan pembuatan situs web Anda adalah hanya untuk menjadikannya sebagai media forum diskusi. Ada banyak pilihan skrip forum dengan ukuran berkas yang jauh lebih kecil dan sederhana namun sudah dapat memenuhi segala fitur yang ada dalam spesifikasi forum diskusi pada umumnya seperti PunBB dan Vanilla Forums. Begitu pula dengan situs web bertema blog. Apabila blog yang Anda buat merupakan blog pribadi yang hanya memiliki satu orang penulis saja (yaitu Anda) dan hanya bertujuan untuk memperkenalkan diri Anda ke dunia maya, maka sistem manajemen konten berbasis berkas seperti Mecha justru lebih Saya rekomendasikan dibandingkan dengan sistem manajemen konten berbasis basis-data seperti WordPress yang terlalu kaya akan fitur (yang tidak akan pernah Anda pakai nantinya).

Memang butuh waktu yang tidak sebentar untuk mampu menguasai sesuatu yang baru. Dan butuh tenaga yang tidak sedikit juga untuk mampu keluar dari zona nyaman, terlebih jika selama ini Anda telah terlanjur mendewakan sistem manajemen konten tertentu. Yang terpenting adalah pelajari dulu setiap produk yang Anda temukan sebelum Anda memutuskan untuk memakainya. Lihat slogan, deskripsi dan profil mereka serta halaman visi dan misi mereka sehingga Anda bisa menentukan apakah Anda termasuk dalam target pasar mereka atau bukan. Sebagai contoh, WordPress memiliki slogan “Blog tool, publishing platform, and CMS.”, Drupal memiliki slogan “Drupal is an open source platform for building amazing digital experiences.” dan/atau “Because we all have different needs, Drupal allows you to create a unique space in a world of cookie-cutter solutions.”, Joomla! memiliki slogan “The flexible platform empowering website creators.” dan PrestaShop memiliki slogan “Launch your online store right now. There’s a solution to suit everyone’s needs.”.

Dengan membaca slogan-slogan mereka saja sebenarnya Anda sudah bisa menyimpulkan tujuan pembuatan dan fungsi dari produk-produk tersebut.

Tentukan Target Kisaran Jumlah Pengunjung ke Situs Anda

Target kisaran jumlah pengunjung berhubungan dengan penggunaan lebar pita. Lebar pita dalam konteks komputer jaringan adalah suatu nilai konsumsi transfer data yang dihitung dalam bit per detik atau yang biasanya disebut dengan bit per second (bps) antara peladen dan klien dalam waktu tertentu. Dalam hubungannya dengan penyedia layanan web hosting, biasanya ukuran lebar pita akan distandarkan menjadi banyaknya data yang ditransfer dari peladen ke klien atau sebaliknya dalam jangka waktu satu bulan.

Ini tidak terbatas pada aktivitas mengunduh saja namun juga termasuk aktivitas mengunggah. Menulis pesan pada formulir kontak dan mengirimkannya merupakan aktivitas “mengunggah data” dan itu akan masuk ke dalam perhitungan lebar pita. Membuka halaman web merupakan aktivitas “mengunduh halaman” (mengunduh berkas HTML, CSS dan JavaScript) dan itu juga akan masuk ke dalam perhitungan lebar pita.

Perhitungan rata-rata lebar pita dalam satu bulan untuk 1000 orang pengunjung dapat dikalkulasikan sebagai berikut:

Pertama-tama, tentukan rata-rata ukuran halaman web Anda, misalnya 1.8 MB. Setiap pengunjung mungkin akan membuka halaman web Anda sebanyak lima kali dalam sehari sebelum kemudian pergi, ini sudah termasuk aktivitas berkirim pesan juga. Jadi Anda kalikan 1.8 MB dengan jumlah kunjungan: 1.8 MB × 5 = 9 MB. Kemudian Anda kalikan 9 MB dengan 1 bulan (sekitar 30 hari): 9 MB × 30 = 270 MB. Kemudian Anda kalikan dengan target jumlah pengunjung ke situs Anda dalam sebulan, misalnya 1000 pengunjung: 270 × 1000 = 270000 MB atau sekitar 270 GB.

Nah, dari hasil perhitungan kasar tersebut Anda bisa menentukan paket hosting yang sesuai dengan spesifikasi situs web dan isi dompet Anda, yaitu paket hosting yang menyediakan batasan lebar pita ± 270 GB per bulan. Saya sebut sebagai perhitungan kasar karena tidak mungkin semua halaman memiliki ukuran 1.8 MB tanpa terkecuali. Karena dalam satu-dua-ratus halaman yang lain mungkin terdapat gambar dan video yang memiliki ukuran berbeda-beda, dengan kumpulan kata dalam artikel yang memiliki jumlah beragam pula. Tidak semua pengunjung akan mengunjungi halaman Anda sebanyak lima kali juga, bisa saja ada yang kurang atau bahkan lebih dari itu. Itu pun masih dalam cakupan pengunjung yang berupa manusia asli, belum pengunjung-pengunjung bot mesin pencari. Satu bulan juga tidak selalu berjumlah 30 hari.

Kita bahkan belum menghitung optimalisasi seperti penetapan jangka waktu kadaluarsa pada expires header. Perhitungan-perhitungan sebelumnya hanyalah asumsi bahwa seluruh halaman web Anda tidak memiliki keterangan waktu kadaluarsa yang spesifik sehingga setiap pengunjung baik yang baru maupun yang lama akan selalu memuat berkas-berkas daring langsung dari peladen, bukan dari tembolok yang tersimpan di komputer masing-masing. Tapi dari sini setidaknya Anda tahu bahwa kebutuhan lebar pita yang diperlukan tidak akan mungkin lebih besar dari 270 GB per bulan.

Meskipun nilainya kurang dari 270 GB per bulan, tapi kebutuhan lebar pita seperti itu tetap saja tidak bisa disebut sebagai kebutuhan yang kecil. Di sini Anda tidak perlu khawatir karena DomaiNesia menyediakan paket hosting termurah dengan pusat data yang berlokasi di Indonesia, Singapura, Amerika, Inggris dan Jepang. Kabar baiknya, semua paket yang tersedia sama sekali tidak memperhitungkan jumlah lebar pita dan sub-domain. Sehingga dengan berlangganan paket hosting dari mereka, Anda tidak perlu pusing lagi memikirkan kebutuhan lebar pita yang ada. Anda hanya perlu fokus kepada domain dan kuota ruang penyimpanan saja.

Kelola Pemasukan Pasif

Ketika Anda menjalankan situs web pribadi seperti Saya, agaknya situs web tersebut tidak akan memberikan pemasukan apa-apa untuk Anda. Oleh karena itu, ada baiknya Anda membuat situs web ke dua, ke tiga, ke empat dan ke lima yang memiliki orientasi kepada peningkatan jumlah pengunjung dengan tujuan untuk mendapatkan pemasukan pasif. Dalam situs web tersebut, Anda bisa menjalankan iklan Google AdSense atau BuySellAds, atau Anda juga bisa membuka ruang pemasangan iklan secara manual. Kalaupun itu tidak akan menambahkan pemasukan yang berarti untuk Anda, setidaknya hasil pemasukan yang didapatkan dari iklan-iklan tersebut bisa Anda gunakan untuk membayar biaya sewa domain dan ruang penyimpanan situs web Anda setiap tahun tanpa harus menyisihkan pengeluaran dari penghasilan Anda di dunia nyata.

Anda juga bisa menambahkan tombol donasi. Akan tetapi praktik semacam ini mungkin perlu pertanggungjawaban yang besar. Karena para donatur tentu memerlukan alasan yang jelas untuk memberikan donasi. Jadi, ciptakanlah alasan tersebut untuk mereka sebelum Anda memutuskan menambahkan tombol donasi. Namun jika sejak awal situs web Anda memang bertujuan untuk memperoleh penghasilan, maka cara ini bisa Anda abaikan. Anda hanya perlu mengambil sebagian kecil dari penghasilan situs web yang Anda kelola untuk membayar biaya sewa ruang penyimpanan dan domain setiap tahunnya.

Ada yang lain?

Labels: ,

Sunday, October 14, 2018

CKEditor 5 Image Upload Adapter

CKEditor 5 Image Upload
Sumber gambar: CKEditor

Menggunakan kode JavaScript <ES6 untuk mengaktifkan fitur pengunggah gambar di CKEditor 5 tanpa layanan Easy Image.

Pembaharuan 2018/12/24: Paket CKEditor 5 Custom Builder untuk pemula sudah tersedia di https://github.com/mecha-cms/c-k-editor.5.

Labels: , , ,

Saturday, October 6, 2018

Lis Berkas dengan PHP `glob`

Kebanyakan dari kita akan menggunakan cara ini untuk mendapatkan daftar berkas pada sebuah direktori, yang mana ini akan menampilkan semua jalur berkas dan folder, namun tidak dengan jalur berkas dan folder yang memiliki awalan ., misalnya pada berkas dengan nama .htaccess:

$files = glob('.\path\to\folder\*');

Untuk mengatasi masalah ini, biasanya kita akan menggabungkan dua pola yaitu .* untuk menelusuri berkas dan folder dengan awalan . dan pola * untuk menelusuri berkas dan folder generik:

$files = array_unique(array_merge(
    glob('.\path\to\folder\.*'),
    glob('.\path\to\folder\*')
));

Kendala sekarang ada pada jalur .\path\to\folder\. dan .\path\to\folder\.. yang sebenarnya tidak kita perlukan, namun muncul juga di dalam daftar. Sehingga kita perlu melakukan usaha ekstra untuk menyingkirkan item tersebut dari daftar:

$files = array_filter(array_unique(array_merge(
    glob('.\path\to\folder\.*'),
    glob('.\path\to\folder\*')
)), function($v) {
    $n = basename($v);
    return $n !== '.' && $n !== '..';
});

Cara paling sederhana untuk menampilkan semua jalur berkas dan folder, termasuk berkas dan folder yang memiliki awalan nama . adalah seperti ini:

$files = glob('.\path\to\folder\{,.}[!.,!..]*', GLOB_BRACE);

Meskipun berkas dengan nama seperti ...foo-bar.baz akan gagal lolos pada pola penelusuran di atas, namun penamaan berkas dengan awalan . lebih dari satu sepertinya sangat jarang ada, jadi Saya kira pola ini cukup aman.


Referensi: Stack Overflow

Labels: , ,

Thursday, September 6, 2018

Menghilangkan CSS dan JavaScript Bawaan Blogger

Pembaharuan sintaks Blogger yang sekarang memungkinkan kita mencegah mesin Blogger untuk menyisipkan kode CSS dan JavaScript bawaan ke dalam hasil keluaran HTML tema. Caranya adalah dengan menambahkan atribut b:css dan b:js dengan nilai false.

<html b:css='false' b:js='false'> … </html>

Namun satu hal yang perlu diingat bahwa beberapa fitur bawaan yang bekerja dengan JavaScript mungkin tidak akan bisa bekerja karena perubahan ini. Fitur-fitur tersebut di antaranya adalah fitur balas komentar, fitur buka-tutup pada widget arsip hierarki, fitur formulir kontak, dan juga fitur ini.

Labels: , , ,

Tuesday, July 31, 2018

Widget HTML Blogger untuk Menyimpan Data Konfigurasi

Metode ini pertama kali Saya dapatkan dari Bung Frangki dimana beliau menggunakan bidang konten pada widget HTML sebagai tempat untuk menyimpan data konfigurasi. Widget tersebut adalah widget yang dibuat secara manual, bukan dengan cara menggunakan fitur antarmuka dari Blogger.

<b:widget id='HTML1' locked='true' title='Related Post' type='HTML' version='1'>
  <b:widget-settings>
    <b:widget-setting name='content'>numPosts: 6,
widgetStyle: 3,
summaryLength: 125</b:widget-setting>
  </b:widget-settings>
  <b:includable id='main'>
    <b:if cond='data:title != ""'>
      <h4 class='widget-title'><data:title/></h4>
    </b:if>
    <div class='widget-content'>
      <script>
      var relatedPostConfig = {<data:content/>};
      </script>
    </div>
  </b:includable>
</b:widget>

Sehingga pada hasil keluaran nantinya akan menjadi seperti ini:

<div class='widget HTML' data-version='1' id='HTML1'>
  <h4 class='widget-title'>Judul Widget</h4>
  <div class='widget-content'>
    <script>
    var relatedPostConfig = {
        numPosts: 6,
        widgetStyle: 3,
        summaryLength: 125
    };
    </script>
  </div>
</div>

Sekarang kita coba lihat kembali bagaimana tag <b:with> bekerja. Selain untuk menampilkan data berupa string, tag ini juga mampu menganggap string objek sebagai data objek. Sehingga jika kita menuliskan data konten sebagai objek Blogger dan menggunakan tag <b:with> sebagai konverternya, maka kita bisa menggunakan teks data konten tersebut sebagai data objek:

<b:widget id='HTML1' title='Profil' type='HTML' version='1'>
  <b:widget-settings>
    <b:widget-setting name='content'>{
    name: "Taufik Nurrohman",
    content: "&lt;p&gt;Saya adalah pemilik blog ini.&lt;/p&gt;",
    links: [{
        title: "Facebook",
        url: "//www.facebook.com/ta.tau.taufik"
    }, {
        title: "Google+",
        url: "//plus.google.com/+TaufikNurrohman"
    }]
}</b:widget-setting>
  </b:widget-settings>
  <b:includable id='main'>
    <b:if cond='data:title != ""'>
      <h4 class='widget-title'><data:title/></h4>
    </b:if>
    <div class='widget-content'>
      <b:with var='config' expr:value='data:content'>
        <h5><data:config.name/></h5>
        <div>
          <data:config.content/>
        </div>
        <h6>Links</h6>
        <b:if cond='data:config.links.size > 0'>
          <ul>
            <b:loop values='data:config.links' var='value'>
              <li><a expr:href='data:value.url'><data:value.title/></a></li>
            </b:loop>
          </ul>
        </b:if>
      </b:with>
    </div>
  </b:includable>
</b:widget>

Referensi: https://productforums.google.com/forum/#!topic/blogger/kIszC8MjUyg;context-place=forum/blogger

Labels: , ,

Wednesday, May 16, 2018

Mengecek Adanya Komentar Balasan pada Komentar Induk di Blogger

Mengingat kembali tentang bagaimana kita membedakan antara komentar induk dengan komentar balasan pada Blogger dapat kita lakukan dengan cara mengecek adanya properti var.inReplyTo pada item komentar yang dimaksud. Properti ini bertugas untuk menyimpan ID komentar induk:

<b:loop values='data:post.comments' var='comment'>
  <b:if cond='data:comment.inReplyTo'>
    <!-- `data:comment` adalah komentar balasan -->
  <b:else/>
    <!-- `data:comment` adalah komentar induk -->
  </b:if>
</b:loop>

Dari sini kita dapat melakukan iterasi ulang di dalam iterasi komentar induk, dan kemudian menyaring anak-anak komentar yang memiliki nilai properti var.inReplyTo (dalam hal ini adalah reply.inReplyTo) yang sama dengan parent.id (dalam hal ini adalah comment.id):

<b:loop values='data:post.comments' var='comment'>
  <b:if cond='!data:comment.inReplyTo'>
    <b:loop values='data:post.comments' var='reply'>
      <b:if cond='data:reply.inReplyTo == data:comment.id'>
        <!-- komentar balasan untuk `data:comment` akan tersedia di sini sebagai `data:reply` -->
      </b:if>
    </b:loop>
  </b:if>
</b:loop>

Ekspresi lambda (fungsi anonim) pada Blogger memungkinkan kita untuk melakukan penyaringan komentar-komentar balasan terhadap ID komentar induk dengan cara yang lebih cepat seperti ini, karena penyaringan data komentar dapat dilakukan sebelum proses iterasi dilakukan:

<b:loop values='data:post.comments' var='comment'>
  <b:if cond='!data:comment.inReplyTo'>
    <b:loop values='data:post.comments filter (i => i.inReplyTo == data:comment.id)' var='reply'>
      <!-- komentar balasan untuk `data:comment` akan tersedia di sini sebagai `data:reply` -->
    </b:loop>
  </b:if>
</b:loop>

Ada satu metode yang menarik dalam fungsi anonim Blogger yaitu metode count. Metode ini memungkinkan kita untuk menghitung jumlah item yang ada setelah proses bersyarat selesai diterapkan pada koleksi data. Sebagai contoh, kode di bawah ini akan menampilkan jumlah total komentar dengan nama penulis Taufik Nurrohman:

Jumlah komentar dari Taufik Nurrohman: <b:eval expr='data:post.comments count (i => i.author == "Taufik Nurrohman")'/>

Dalam bahasa pemrograman, kita dapat melakukan sesuatu seperti ini untuk menghitung jumlah komentar dari Taufik Nurrohman, yang mana ini tidak akan dapat kita lakukan pada Blogger sebelum adanya fitur fungsi anonim:

let i = 0;
post.comments.forEach(comment => {
    if (comment.author == 'Taufik Nurrohman') {
        ++i;
    }
});

console.log('Jumlah komentar dari Taufik Nurrohman: ' + i);

Metode count pada fungsi anonim akan lebih sesuai jika disamakan dengan metode filter dan properti length pada JavaScript seperti ini:

let i = post.comments.filter(i => i.author == 'Taufik Nurrohman').length;

console.log('Jumlah komentar dari Taufik Nurrohman: ' + i);

Cara Mengecek Apakah Komentar Induk Memiliki Komentar Balasan atau Tidak

Sebuah komentar dari seorang pembaca bernama Satank Mkr pada artikel Membuat Fitur Komentar Berbalas (Threaded Comments) pada Blogger dengan Fungsional yang Asli kurang lebih menanyakan tentang bagaimana caranya menambahkan elemen pembungkus khusus yang akan melingkupi seluruh komentar balasan, sehingga jika terdapat setidaknya satu buah komentar balasan di bawah komentar induk, maka komentar-komentar balasan tersebut akan dibungkus dengan elemen HTML tertentu. Berikut adalah ilustrasi yang beliau maksudkan:

<b:if cond='data:post.numberOfComments > 0'>
  <ul class='comments'>
    <b:loop values='data:post.comments' var='comment'>
      <li class='comment'> … </li>
    </b:loop>
  </ul>
</b:if>
<ul class="comments">
  <li class="comment"> … </li>
  <li class="comment"> … </li>
  <li class="comment"> … </li>
</ul>

Melihat pada contoh di atas, akan sangat mudah untuk menambahkan elemen pembungkus <ul class="comments"> karena Blogger memiliki properti numberOfComments pada data:post yang bertugas untuk menyimpan jumlah keseluruhan komentar yang ada. Akan tetapi, kita tidak memiliki properti khusus untuk menghitung jumlah komentar balasan melalui data:comment, sehingga untuk menentukan apakah sebuah komentar induk memiliki komentar balasan atau tidak akan mustahil tanpa adanya properti khusus; katakanlah var.numReplies seperti ini:

<b:if cond='data:post.numberOfComments > 0'>
  <ul class='comments'>
    <b:loop values='data:post.comments' var='comment'>
      <li class='comment'> … </li>
        <b:if cond='data:comment.numReplies > 0'>
          <ul class='comment-replies'>
            <b:loop values='data:post.comments filter (i => i.inReplyTo == data:comment.id)' var='reply'>
              <li class='reply'> … </li>
            </b:loop>
          </ul>
      </b:if>
    </b:loop>
  </ul>
</b:if>
<ul class="comments">
  <li class="comment"> … </li>
  <li class="comment"> … </li>
  <li class="comment">
    …
    <ul class='comment-replies'>
      <li class='reply'> … </li>
      <li class='reply'> … </li>
    </ul>
  </li>
  <li class="comment"> … </li>
</ul>

Metode count datang menyelamatkan! Karena metode ini mampu menghitung jumlah komentar yang ada setelah proses bersyarat diterapkan pada data komentar, maka kita dapat menghitung jumlah komentar balasan terkait dengan komentar induk dengan cara seperti ini:

<b:with var='numReplies' value='data:post.comments count (i => i.inReplyTo == data:comment.id)'>
  Jumlah komentar balasan: <data:numReplies/>
</b:with>

Untuk menerapkannya sebagai ekspresi kondisional di dalam iterasi komentar, kita bisa menuliskannya seperti ini:

<b:if cond='data:post.numberOfComments > 0'>
  <ul class='comments'>
    <b:loop values='data:post.comments' var='comment'>
      <li class='comment'> … </li>
        <b:with var='numReplies' value='data:post.comments count (i => i.inReplyTo == data:comment.id)'>
          <b:if cond='data:numReplies > 0'>
            <ul class='comment-replies'>
              <b:loop values='data:post.comments filter (i => i.inReplyTo == data:comment.id)' var='reply'>
                <li class='reply'> … </li>
              </b:loop>
            </ul>
          </b:if>
        </b:with>
      </b:if>
    </b:loop>
  </ul>
</b:if>

Atau seperti ini juga bisa:

<b:if cond='data:post.numberOfComments > 0'>
  <ul class='comments'>
    <b:loop values='data:post.comments' var='comment'>
      <li class='comment'> … </li>
        <b:with var='replies' value='data:post.comments filter (i => i.inReplyTo == data:comment.id)'>
          <b:if cond='data:replies.size > 0'>
            <ul class='comment-replies'>
              <b:loop values='data:replies' var='reply'>
                <li class='reply'> … </li>
              </b:loop>
            </ul>
          </b:if>
        </b:with>
      </b:if>
    </b:loop>
  </ul>
</b:if>

Labels: , ,

Wednesday, September 3, 2014

Membuat Agar Tinggi Kontainer Mengikuti Tinggi Latar Gambarnya yang Responsif/Adaptif

Problem in Fluid Container with Fluid Background Image

Saya agak kesulitan untuk menemukan istilah yang tepat untuk menyatakan “latar gambar yang ukurannya bisa menyesuaikan diri dengan kontainer setiap kali ukuran kontainer berubah”, jadi Saya sebut saja sebagai ‘responsif’ atau ‘adaptif’, meskipun istilah tersebut sebenarnya kurang tepat. Istilah fluid lebih tepat.

Ketika kita ingin membuat sebuah gambar menjadi fluid, kita bisa mengeset lebar gambar tersebut ke dalam satuan persen dan tinggi gambar sebagai auto untuk menjaga agar ukuran tingginya tetap proporsional:

img {
  width:100%;
  height:auto;
}

Lihat Demo

Bagaimana dengan latar gambar? Kita bisa menggunakan properti background-size dengan nilai cover atau 100% auto. Hanya saja, di sini masalahnya adalah kita tidak bisa membuat tinggi kontainer mengikuti tinggi latar gambar setiap kali lebar kontainer berubah:

div {
  background:transparent url('path/to/image.jpg') no-repeat 50% 0;
  background-size:100% auto;
  height:615px;
}

Lihat Demo

Rasio Ukuran Tinggi Proporsional

Ada sebuah metode yang bisa digunakan untuk membuat tinggi kontainer gambar bisa mengikuti tinggi latar gambar yang ada di dalamnya (di belakangnya), yaitu dengan cara mengeset ukuran tinggi kontainer menjadi 0 dan menentukan nilai sebuah sisi padding vertikal dalam satuan persen untuk menciptakan ukuran tinggi kontainer palsu. Nilainya berupa hasil perbandingan antara tinggi gambar dengan lebar gambar dalam bentuk persen. Perhitungannya seperti ini:

Rasio = (Tinggi / Lebar * 100) + '%';

Atau jika diterjemahkan ke dalam bahasa yang sederhana adalah: “Tinggi proporsional X adalah sama dengan sekian persen dari lebarnya.”

Saya menggunakan gambar dengan ukuran lebar 800 piksel dan tinggi 615 piksel pada halaman demo, jadi:

Rasio Tinggi = (615 / 800 * 100) + '%';
Rasio Tinggi = 76.875%;
div {
  background:transparent url('path/to/image.jpg') no-repeat 50% 0;
  background-size:100% auto;
  height:0; /* Set tinggi kontainer menjadi `0` */
  padding-bottom:76.875%; /* Gunakan `padding` untuk menciptakan dimensi tinggi */
}

Lihat Demo

Catatan: Jika Anda telah menerapkan CSS Universal Box-Sizing, Saya sarankan Anda untuk menambahkan properti box-sizing dengan nilai content-box pada kontainer untuk mengembalikan logika box-model ke keadaan semula. Meskipun ketika Saya coba tanpa menambahkan properti itu sebenarnya tidak ada masalah. Ini cuma untuk memantapkan saja:

div {
  background:transparent url('path/to/image.jpg') no-repeat 50% 0;
  background-size:100% auto;
  height:0;
  padding-bottom:76.875%;
  box-sizing:content-box;
}

Menambahkan Konten ke dalam Kontainer

Anda memerlukan sebuah elemen tambahan untuk diposisikan sebagai elemen absolute karena kontainer yang sekarang sudah tidak memiliki ruang lagi (tinggi kontainer sekarang adalah 0 piksel):

HTML

<div class="container">
  <div class="container-content">Konten di sini…</div>
</div>

CSS

.container {
  background:transparent url('path/to/image.jpg') no-repeat 50% 0;
  background-size:100% auto;
  height:0;
  padding-bottom:76.875%;
  box-sizing:content-box;
  position:relative;
}

.container-content {
  position:absolute;
  top:0;
  right:0;
  bottom:0;
  left:0;
  overflow:auto;
}

Lihat Demo

Labels: , ,

Sunday, July 6, 2014

Fake Link

HTML

<form class="fake-link" action="//example.org" method="get" target="_blank">
  <input name="aff" type="hidden" value="1234567">
  <button type="submit">affiliate link</button>
</form>

CSS

.fake-link,
.fake-link button {
  margin:0;
  padding:0;
  width:auto;
  height:auto;
  background:none;
  border:none;
  font:inherit;
  text-transform:none;
  display:inline;
}
.fake-link button {
  color:blue;
  cursor:pointer;
}
.fake-link button::-moz-focus-inner {
  margin:0;
  padding:0;
  border:none;
  outline:none;
}
.fake-link button:focus,
.fake-link button:hover {text-decoration:underline}
.fake-link button:active {color:red}

Demo

Lihat Demo

Labels: , ,

Saturday, May 17, 2014

Kondisional Halaman “selected” pada Widget Label Blogspot Tidak Konsisten

Saya anggap Anda sedang memakai widget Label/Kategori yang masih murni merupakan bawaan dari Blogger. Saat kita mengunjungi halaman label, pada bagian tautan label seharusnya akan berubah menjadi elemen <span> yang tidak bisa diklik seperti ini:

Label Widget in Blogspot
Tanda bahwa halaman label “JavaScript” sedang dikunjungi.

Ini bagus, karena widget dapat memberikan informasi kepada kita mengenai di mana kita sedang berada saat itu. Akan tetapi, ketika kita mengeklik tautan menuju posting yang lebih lama, tanda tersebut malah hilang:

Broken Label Widget
Tanda yang menyatakan bahwa halaman label “JavaScript” sedang dikunjungi rusak pada halaman berikutnya.

Ini tidak bagus. Tanda label tersebut seharusnya bisa tetap ada meskipun kita sudah berpindah-pindah ke halaman yang lain asalkan kita masih berada dalam halaman indeks kategori/label yang sama. Ini terjadi karena kondisional penanda pada widget label sudah salah sejak awal. Perhatikan bagian yang Saya tandai:

<b:widget id='Label1' locked='false' title='Label' type='Label'>
  <b:includable id='main'>
    <b:if cond='data:title'>
      <h2><data:title/></h2>
    </b:if>
    <div expr:class='&quot;widget-content &quot; + data:display + &quot;-label-widget-content&quot;'>
      <b:if cond='data:display == &quot;list&quot;'>
        <ul>
          <b:loop values='data:labels' var='label'>
            <li><b:if cond='data:blog.url == data:label.url'>                <span expr:dir='data:blog.languageDirection'><data:label.name/></span>
              <b:else/>
                <a expr:dir='data:blog.languageDirection' expr:href='data:label.url'><data:label.name/></a>
              </b:if>
              <b:if cond='data:showFreqNumbers'>
                <span dir='ltr'>(<data:label.count/>)</span>
              </b:if>
            </li>
          </b:loop>
        </ul>
      <b:else/>
        <b:loop values='data:labels' var='label'>
          <span expr:class='&quot;label-size label-size-&quot; + data:label.cssSize'><b:if cond='data:blog.url == data:label.url'>              <span expr:dir='data:blog.languageDirection'><data:label.name/></span>
            <b:else/>
              <a expr:dir='data:blog.languageDirection' expr:href='data:label.url'><data:label.name/></a>
            </b:if>
            <b:if cond='data:showFreqNumbers'>
              <span class='label-count' dir='ltr'>(<data:label.count/>)</span>
            </b:if>
          </span>
        </b:loop>
      </b:if>
      <b:include name='quickedit'/>
    </div>
  </b:includable>
</b:widget>

Menggunakan perbandingan URL sebagai kondisional halaman label tidak akan berhasil, karena URL pada setiap halaman itu pasti akan berubah-ubah meskipun kita masih berada pada kategori/label yang sama. Untuk memperbaikinya, ganti kode yang Saya beri tanda dengan ini:

<b:if cond='data:blog.searchLabel == data:label.name'>

Simpan perubahan.

Labels: , ,

PHP “include” ke Variabel

Menyisipkan HTML mentah atau statis ke dalam variabel itu mudah. Pakai saja file_get_contents():

$html = file_get_contents('page.html');

echo $html; // tampilkan hasil

Tapi menyisipkan HTML dinamis yang di dalamnya berisi perintah-perintah PHP ke dalam variabel itu tidaklah mudah. Pertama-tama kita harus menyisipkan berkas tersebut ke dalam halaman, tapi jangan sampai berkas tersebut tampil. Kita hanya akan mengambil hasil render perintah PHP di dalam berkas HTML tersebut untuk kemudian bisa kita simpan ke dalam variabel seperti contoh di atas:

ob_start(); // [1]
include 'page.php'; // [2]
$html = ob_get_contents(); // [3]
ob_end_clean(); // [4]
  1. Memulai output buffering.
  2. Masukkan berkas HTML dinamis ke halaman untuk melakukan render.
  3. Simpan hasil render ke dalam variabel $html.
  4. Matikan output buffering.

Setelah hasil render tersimpan ke dalam variabel, Anda bisa menggunakan variabel tersebut seperti biasa sebagaimana variabel bertipe string digunakan:

echo $html;

Labels: , ,

Friday, April 4, 2014

PHP Konversi Array Menjadi Objek/Objek Menjadi Array

1. Array ke Objek

Fungsi

function array_to_object($array) {
    if(is_array($array)) {
        return (object) array_map(__FUNCTION__, $array);
    } else {
        return $array;
    }
}

Contoh Kasus

Sebelum dikonversi…

$test = array(
    'A' => 'Test A',
    'B' => 'Test B',
    'C' => array(
        'CA' => 'Test CA',
        'CB' => array(
            'CBA' => 'Test CBA'
        )
    ),
    'D' => 'Test D'
);

// Pemanggilan...
echo $test['A']; // Hasil => `Test A`
echo $test['C']['CB']; // Hasil => `Test CB`
echo $test['C']['CB']['CBA']; // Hasil => `Test CBA`

Sesudah dikonversi…

...

$object_test = array_to_object($test);

// Pemanggilan...
echo $object_test->A; // Hasil => `Test A`
echo $object_test->C->CB; // Hasil => `Test CB`
echo $object_test->C->CB->CBA; // Hasil => `Test CBA`

2. Objek ke Array

Fungsi

function object_to_array($object) {
    if(is_object($object)) {
        $object = get_object_vars($object);
    }
    if(is_array($object)) {
        return array_map(__FUNCTION__, $object);
    } else {
        return $object;
    }
}

Contoh Kasus

Sebelum dikonversi…

$test = new stdClass;
$test->A = 'Test A';
$test->B = 'Test B';
$test->C = new stdClass;
$test->C->CA = 'Test CA';
$test->C->CB = new stdClass;
$test->C->CB->CBA = 'Test CBA';
$test->D = 'Test D';

// Pemanggilan
echo $test->A; // Hasil => `Test A`

Sesudah dikonversi…

...

$array_test = object_to_array($test);

// Pemanggilan
echo $array_test['A']; // Hasil => `Test A`

Labels: , ,

Wednesday, September 25, 2013

Membuat Formulir Kontak Google Doc Agar Bisa Mengirimkan Datanya Langsung ke Kotak Pesan Email

Posting ini membutuhkan pembaharuan → Google Apps Script – Error: “DocsList” not Defined

Sebenarnya Google Doc bukan merupakan layanan untuk membuat aplikasi-aplikasi semacam ini. Membuat formulir kontak menggunakan Google Doc hanya bisa menampilkan pesan di spreadsheet, dan bukannya langsung menuju kotak masuk pesan seperti yang kita kehendaki. Karena fungsi utama Google Doc pada dasarnya memang bukan untuk membuat formulir kontak semacam itu, melainkan untuk membuat formulir yang terintegrasi dengan dokumen, sehingga ini memungkinkan pengguna untuk bisa mengirimkan data secara online. Dan dari formulir itulah data bisa langsung terkirim dan akan tersimpan ke dalam dokumen berbentuk tabel.

Blogger Xpertise memiliki solusi agar data pesan yang dikirimkan melalui formulir Google Doc bisa langsung terkirim menuju pesan masuk email Anda seperti ini:

Contact results - Google Drive
Data kontak langsung terlihat di pesan masuk.

Di sini Saya akan menjelaskan langkah-langkah pembuatan formulir kontak dimulai dari awal sampai akhir. Ada dua bagian utama yang harus Anda kerjakan di sini, yaitu membuat formulir kontak dan mengelola spreadsheet yang terkait dengan formulir kontak tersebut.

Membuat Formulir Kontak

Di sini Anda akan diajak untuk membuat formulir kontak menggunakan Google Doc.

Pertama-tama buka halaman dasbor Google Drive Anda, lalu buat sebuah formulir:

Create a new form - Google Drive
Membuat formulir baru.

Tentukan judul dan deskripsi formulir, serta buat beberapa kotak pertanyaan yang umum terdapat pada formulir kontak di web. Misalnya: Perihal, Nama, Email, Alamat Web dan Pesan:

Form configuration - Google Drive
Membuat formulir.

Pada bagian Laman Konfirmasi, tuliskan pesan terima kasih yang ingin Anda tampilkan ketika pesan telah berhasil terkirim:

Sent message setup - Google Drive
Menyunting pesan ucapan terima kasih.

Klik tombol Kirim Formulir. Akan muncul kotak dialog baru. Klik tombol Sematkan. Di situ Anda akan diberi kode embed formulir kontak yang bisa Anda pasang pada halaman web Anda. Atau Anda juga bisa melihat hasil jadinya pada halaman formulir khusus dengan menekan tombol Lihat Bentuk Jadi di panel atas, tepat di bawah menu utama.

Klik tombol Pilih Tujuan Tanggapan, lalu cek opsi Spreasheet baru. Berikan judul, misalnya Data Tanggapan:

Create new spreadsheet - Google Drive
Membuat spreadsheet baru.

Klik tombol Buat. Tunggu sampai tombol Lihat Tanggapan muncul. Pada tampilan Google Doc saat ini, tombol Lihat Tanggapan akan muncul untuk menggantikan tombol Pilih Tujuan Tanggapan. Klik tombol tersebut untuk melihat spreadsheet formulir kontak Anda.

Mengelola Data yang Masuk

Setelah spreadsheet tercipta, sekarang adalah saatnya untuk mengelola data yang masuk agar setiap kali data baru terkirim ke dokumen, maka salinan data tersebut bisa langsung masuk ke kotak pesan email.

Pilih menu Alat » Editor skrip:

Script editor menu - Google Drive
Editor skrip.

Anda akan dibawa menuju halaman proyek tak berjudul. Pada formulir bernama Kode.gs tempelkan JavaScript ini:

function sendFormByEmail(e) {

  // Tentukan teks subjek/perihal yang nantinya akan muncul pada pesan masuk email setiap kali pesan baru terkirim
  var emailSubject = "PESAN BARU!";

  // Tentukan alamat email di sini atau beberapa alamat email sekaligus yang dipisahkan dengan tanda koma
  var yourEmail = "email@domain.com";

  // Masukkan kunci spreadsheet yang terhubung dengan formulir kontak ini
  // Bisa ditemukan pada URL ketika Anda melihat spreadsheet tersebut
  var docKey = "0Ah0bOy8H_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

  // Jika Anda menginginkan skrip ini mengirim data secara otomatis ke semua editor sheet, set nilainya menjadi `1`
  // Jika Anda hanya ingin mengirimkan data ke `yourEmail`, set nilainya menjadi `0`
  var useEditors = 1;

  // Apakah Anda telah menambahkan kolom yang ternyata tidak Anda gunakan pada formulir?
  // Jika ya, set nilai ini ke nomor urut kolom terakhir yang Anda tambahkan pada spreadsheet.
  // Sebagai contoh: Kolom `C` ada pada nomor urut ke 3
  var extraColumns = 0;

  if (useEditors) {
    var editors = DocsList.getFileById(docKey).getEditors();
    var notify = (editors) ? editors.join(',') : yourEmail;
  } else {
    var notify = yourEmail;
  }

  // Variabel `e` memegang semua data di dalam array.
  // Loop semua data di dalam array dan sisipkan nilainya ke pesan.
  var s = SpreadsheetApp.getActive().getSheetByName("Data1");
  if (extraColumns) {
    var headers = s.getRange(1, 1, 1, extraColumns).getValues()[0];
  } else {
    var headers = s.getRange(1, 1, 1, s.getLastColumn()).getValues()[0];
    var message = "";
  }
  for (var i in headers) {
    message += headers[i] + ' = ' + e.values[i].toString() + '\n';
  }
  MailApp.sendEmail(notify, emailSubject, message);
}

Tentukan subjek email, alamat email dan kunci/ID spreadsheet yang tadi Anda buat. ID spreadsheet bisa Anda temukan pada URL spreadsheet terkait:

https://docs.google.com/spreadsheet/ccc?key=0Ah0bOy8H_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#gid=0

Data1 adalah nama sheet. Pastikan nilainya sama dengan sheet terkait:

Sheet rename - Goole Drive
Memberi nama sheet.

Sekarang pilih menu Sumber Daya » Pemicu proyek saat ini. Akan muncul kata “No triggers set up. Click to add one now”. Klik pada tautan tersebut untuk menambahkan trigger. Pada kolom Events, pastikan opsi yang terpilih adalah From spreadsheet dan On form submit.

Script trigger authorization - Google Drive
Pemicu proyek.…

Klik opsi notification. Pada opsi ke dua di kotak dialog Execution failure notifications, pilih immediately.

Klik OK.

Akan muncul pesan authorisasi skrip. Klik tombol Terima/Authorize. Ini akan menyetujui authorisasi untuk trigger yang Anda buat.

Anda akan dibawa kembali menuju layar editor. Klik Simpan/Save. Sekarang Anda sudah bisa mencoba mengirimkan pesan melalui formulir kontak buatan Anda. Pembuatan formulir kontak ini sudah selesai.

Setelah ini mungkin Anda akan menerima pesan error ke kotak pesan email. Beruntung jika tidak. Selebihnya hanyalah mengenai pemecahan masalah error yang terjadi. Saya pribadi belum pernah mengalami masalah-masalah yang rumit. Jadi Saya masih belum bisa membuat daftar kemungkinan error yang terjadi di sini. Berikut ini adalah beberapa hal yang perlu diperhatikan menurut Blogger Xpertise:

  1. Pastikan nama sheet tidak mengandung karakter spasi. Ini mungkin bisa menimbulkan errorSheet Saya mengandung karakter spasi, tetapi Saya tidak mengalami masalah itu.
  2. Jangan sampai ada kolom yang kosong yang Anda tambahkan ke dalam spreadsheet kontak. Ini akan menciptakan masalah pada perataan data yang masuk serta membuat skrip gagal bekerja ⇐ Saya mengalami masalah ini sebelumnya gara-gara menambahkan dan memindahkan kolom baru, serta karena menambahkan field pertanyaan baru pada formulir kontak yang Saya buat. Jadi pastikan formulir yang Anda buat sudah benar-benar menetap.
  3. Nama kolom terkadang juga dapat menimbulkan masalah. Pastikan Anda membuat nama kolom/pertanyaan formulir dengan karakter teks yang standar.

Menonaktifkan Notifikasi pada Kiriman Email Spam

Karena formulir kontak ini tidak memiliki captcha maka seringkali Anda akan mendapatkan pesan-pesan aneh yang datang entah dari mana. Berikut ini adalah sebuah cara untuk membuat agar pesan notifikasi tidak dikirimkan ke email Anda jika submisi pesan yang dilakukan terdeteksi sepagai spam:

function sendFormByEmail(e) {

  // Tentukan teks subjek/perihal yang nantinya akan muncul pada pesan masuk email setiap kali pesan baru terkirim
  var emailSubject = "PESAN BARU!";

  // Tentukan alamat email di sini atau beberapa alamat email sekaligus yang dipisahkan dengan tanda koma
  var yourEmail = "email@domain.com";

  // Masukkan kunci spreadsheet yang terhubung dengan formulir kontak ini
  // Bisa ditemukan pada URL ketika Anda melihat spreadsheet tersebut
  var docKey = "0Ah0bOy8H_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

  // Jika Anda menginginkan skrip ini mengirim data secara otomatis ke semua editor sheet, set nilainya menjadi `1`
  // Jika Anda hanya ingin mengirimkan data ke `yourEmail`, set nilainya menjadi `0`
  var useEditors = 1;

  // Apakah Anda telah menambahkan kolom yang ternyata tidak Anda gunakan pada formulir?
  // Jika ya, set nilai ini ke nomor urut kolom terakhir yang Anda tambahkan pada spreadsheet.
  // Sebagai contoh: Kolom `C` ada pada nomor urut ke 3
  var extraColumns = 0;

  if (useEditors) {
    var editors = DocsList.getFileById(docKey).getEditors();
    var notify = (editors) ? editors.join(',') : yourEmail;
  } else {
    var notify = yourEmail;
  }

  // Cek pesan spam: `0` berarti tidak ada spam
  var isSpam = 0;

  // Isi dengan pola-pola spam yang ingin Anda blokir dalam format regular ekspresi
  var spamKeywords = [
    /beijing escort|louis vuitton|www\.nama_web_1\.com|www\.nama_web_2\.com/i,
    /(buy|cheap)(ing|est)/i,
    /fuck|shit|sex|xxx/i
    ...
    ...
  ];

  // Variabel `e` memegang semua data di dalam array.
  // Loop semua data di dalam array dan sisipkan nilainya ke pesan.
  var s = SpreadsheetApp.getActive().getSheetByName("Data1");
  if (extraColumns) {
    var headers = s.getRange(1, 1, 1, extraColumns).getValues()[0];
  } else {
    var headers = s.getRange(1, 1, 1, s.getLastColumn()).getValues()[0];
    var message = "";
  }
  for (var i in headers) {
    message += headers[i] + ' = ' + e.values[i].toString() + '\n';
    for (var j = 0, sk = spamKeywords.length; j < sk; ++j) {
      // Jika data terdeteksi sebagai spam, tingkatkan nilai variabel `isSpam`
      if (spamKeywords[j].test(e.values[i].toString())) isSpam++;
    }
  }
  if (isSpam === 0) { // Kirimkan notifikasi jika nilai `isSpam` adalah `0`
    MailApp.sendEmail(notify, emailSubject, message);
  }
}

Anda bisa mengatur pola spam pada variabel spamKeywords sesuka hati dengan jumlah pola sebanyak yang Anda mau dalam bentuk regular ekspresi. Jika Anda tidak mengerti atau belum menguasai regular ekspresi, Anda bisa menggunakan deteksi spam yang lebih sederhana menggunakan pengecekan indeks teks:

var spamKeywords = [
  "beijing escort"
  "louis vuitton"
  "www.nama_web_1.com",
  "www.nama_web_2.com",
  "buy",
  "cheap",
  ...
  ...
];
if (e.values[i].toString().indexOf(spamKeywords[j]) > -1) isSpam++;

Walaupun begitu, cara ini hanya bisa digunakan untuk mencegah terkirimnya notifikasi pesan masuk saja jika pesan yang dikirimkan adalah pesan spam, dan tidak bisa mencegah terkirimnya data pesan spam ke dalam spreadsheet. Saya sarankan Anda untuk secara rutin membuka sheet terkait formulir kontak ini minimal setiap satu bulan sekali untuk menghapus data-data yang tidak diperlukan. Karena Saya tidak bisa menjamin tentang apa yang akan terjadi pada data Google Doc Anda jika jumlah data yang masuk nanti sudah terlalu besar.

Bacaan Lebih Lanjut

Labels: , , ,

Tuesday, June 11, 2013

Performa Widget Random Post

Saya harus mengatakan bahwa random post itu sangat buruk dalam hal performa. Kerja mereka sangat lambat dan membuang-buang tenaga, karena mereka bekerja dengan cara memanggil feed posting berukuran sangat besar! Kemudian mereka hanya akan menampilkannya sebagian saja secara acak:

<script src='/feeds/posts/default?alt=json-in-script&max-results=99999&callback=randomPosts'></script>

Bagi Anda para pemakai mungkin tidak pernah tahu mengenai ini, tetapi sebenarnya setiap pemakai widget random post —termasuk juga — tanpa sadar telah menerima beban begitu besar. Ketika Anda membuka halaman tunggal dimana terdapat widget random post di dalamnya, pada saat yang bersamaan Anda juga sebenarnya sedang membuka semua posting yang telah Anda terbitkan dalam satu waktu. Mengapa? Karena parameter max-results pada feed menunjukkan angka 99999 yang artinya bahwa semua feed posting akan “diusahakan” untuk dipanggil (diusahakan, karena jumlah posting yang kita terbitkan tidak mungkin mencapai angka sebesar itu).

Mengacak Indeks Permulaan

Saya menyadari terdapat sebuah peluang untuk menciptakan widget random post yang lebih ringan dan cepat dimuat. Dibandingkan memuat semua posting dan memilah beberapa posting secara acak untuk ditampilkan, akan lebih baik jika kita mengacak nilai start-index pada feed sehingga kita bisa memuat posting dari urutan sembarang:

function randomPosts(json) {
    // Ubah JSON menjadi HTML...
}

var startIndex = Math.round(Math.random() * 9999); // Membuat angka acak
document.write('<scr' + 'ipt src="/feeds/posts/summary?alt=json-in-script&start-index=' + startIndex + '&max-results=7&callback=randomPosts"></scr' + 'ipt>');

Ada satu masalah kecil yang mungkin akan mengganggu. Jika angka acak yang tercipta nilainya lebih besar dari total posting blog Anda saat ini, maka widget random post ini tidak akan bekerja. Oleh karena itu kita harus membatasi angka acak yang tercipta, yaitu tidak boleh kurang dari 1 dan tidak boleh lebih dari “total posting dikurangi jumlah posting yang ingin ditampilkan”.

Untuk mendapatkan jumlah posting secara keseluruhan kita bisa mengambilnya melalui objek json.feed.openSearch$totalResults.$t:

// Konfigurasi
var homePage = 'http://nama_blog.blogspot.com',
    maxResults = 7;

// Fungsi untuk menggenerasikan angka acak dengan batasan minimal dan maksimal
function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

// Mendapatkan indeks pemulai yang aman untuk memanggil feed utama
function createRandomPostsStartIndex(json) {
    // Buat angka acak dengan nilai tidak boleh kurang dari `1` dan tidak boleh lebih dari `total posting - posting yang ingin ditampilkan`
    var startIndex = getRandomInt(1, (json.feed.openSearch$totalResults.$t - maxResults));    // Tampilkan pesan log
    console.log('Get the post feed start from ' + startIndex + ' until ' + (startIndex + maxResults));
}

document.write('<scr' + 'ipt src="' + homePage + '/feeds/posts/summary?alt=json-in-script&max-results=0&callback=createRandomPostsStartIndex"></scr' + 'ipt>');

Muat ulang widget Anda berkali-kali. Seharusnya Anda akan melihat pesan log indeks pemanggilan posting yang berubah-ubah seperti ini:

Showing log message to indicate random number for feed start index purpose.
Angka acak yang Saya tandai akan kita manfaatkan untuk memanggil indeks feed secara acak.

Kita akan menggunakan angka acak itu sebagai angka pemulai pada parameter start-index seperti ini:

function createRandomPostsStartIndex(json) {
    var startIndex = getRandomInt(1, (json.feed.openSearch$totalResults.$t - maxResults));
    document.write('<scr' + 'ipt src="' + homePage + '/feeds/posts/summary?alt=json-in-script&orderby=updated&start-index=' + startIndex + '&max-results=' + maxResults + '&callback=randomPosts"></scr' + 'ipt>');
}

Pada bagian akhir URL feed terdapat parameter callback=randomPosts. randomPosts di sini merupakan fungsi utama yang akan kita buat untuk mengubah JSON Blogger menjadi widget:

function randomPosts(json) {
    var link, ct = document.getElementById('random-post-container'),
        entry = json.feed.entry,
        skeleton = "<ul>";
    for (var i = 0, len = entry.length; i < len; i++) {
        for (var j = 0, jen = entry[i].link.length; j < jen; j++) {
            if (entry[i].link[j].rel == "alternate") {
                link = entry[i].link[j].href;
            }
        }
        skeleton += '<li><a href="' + link + '">' + entry[i].title.$t + '</a></li>';
    }
    ct.innerHTML = skeleton + '</ul>';
}

Sentuhan akhir, acak urutan posting yang ditampilkan menggunakan fungsi ini sehingga daftar posting yang ditampilkan nantinya akan semakin acak urutannya:

// Fungsi untuk mengacak array
function shuffleArray(arr) {
    var i = arr.length, j, temp;
    if (i === 0) return false;
    while (--i) {
        j = Math.floor(Math.random() * (i + 1));
        temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    return arr;
}

// Widget
function randomPosts(json) {
    var link, ct = document.getElementById('random-post-container'),
        entry = shuffleArray(json.feed.entry),
        skeleton = "<ul>";
    for (var i = 0, len = entry.length; i < len; i++) {
        for (var j = 0, jen = entry[i].link.length; j < jen; j++) {
            if (entry[i].link[j].rel == "alternate") {
                link = entry[i].link[j].href;
            }
        }
        skeleton += '<li><a href="' + link + '">' + entry[i].title.$t + '</a></li>';
    }
    ct.innerHTML = skeleton + '</ul>';
}

Hasil Akhir

<div id='random-post-container'>Memuat...</div>

<script>
//<![CDATA[
// Feed configuration
var homePage = 'http://nama_blog.blogspot.com',
    maxResults = 7,
    containerId = 'random-post-container';
// Function to generate random number limited from `min` to `max`
// Used to create a valid and safe random feed `start-index`
function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}
// Function to shuffle arrays
// Used to randomize order of the generated JSON feed
function shuffleArray(arr) {
    var i = arr.length, j, temp;
    if (i === 0) return false;
    while (--i) {
        j = Math.floor(Math.random() * (i + 1));
        temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    return arr;
}
// Get a random start index
function createRandomPostsStartIndex(json) {
    var startIndex = getRandomInt(1, (json.feed.openSearch$totalResults.$t - maxResults));
    // console.log('Get the post feed start from ' + startIndex + ' until ' + (startIndex + maxResults));
    document.write('<scr' + 'ipt src="' + homePage + '/feeds/posts/summary?alt=json-in-script&orderby=updated&start-index=' + startIndex + '&max-results=' + maxResults + '&callback=randomPosts"></scr' + 'ipt>');
}
// Widget's main function
function randomPosts(json) {
    var link, ct = document.getElementById(containerId),
        entry = shuffleArray(json.feed.entry),
        skeleton = "<ul>";
    for (var i = 0, len = entry.length; i < len; i++) {
        for (var j = 0, jen = entry[i].link.length; j < jen; j++) {
            if (entry[i].link[j].rel == "alternate") {
                link = entry[i].link[j].href;
            }
        }
        skeleton += '<li><a href="' + link + '">' + entry[i].title.$t + '</a></li>';
    }
    ct.innerHTML = skeleton + '</ul>';
}
document.write('<scr' + 'ipt src="' + homePage + '/feeds/posts/summary?alt=json-in-script&max-results=0&callback=createRandomPostsStartIndex"></scr' + 'ipt>');
//]]>
</script>

Lihat Demo Demo dengan Thumbnail dan Deskripsi

Ketika Posting Acak Dikirim Secara Langsung oleh Server

Dalam sebuah forum Saya pernah menemukan seseorang yang mencoba menyarankan Blogger untuk menyertakakan parameter orderby baru berupa random dan popular. Saya pikir ini adalah ide yang bagus. Karena berdasarkan dokumentasi, kalau tidak salah hanya ada dua macam nilai untuk parameter orderby, yaitu published dan updated:

http://nama_blog.blogspot.com/feeds/posts/summary?orderby=published http://nama_blog.blogspot.com/feeds/posts/summary?orderby=updated

Jika suatu saat Blogger merealisasikan masukan dari pengguna ini, maka kita tidak perlu lagi menerapkan hack semacam ini. Cukup dengan membuat widget recent post biasa, kita bisa mengubah parameter URL feed dari orderby=published menjadi orderby=random untuk menciptakan widget random post atau orderby=popular untuk menciptakan widget posting populer. Tapi kasus ini masih dalam tahap “seandainya”.

Labels: , , , ,

Friday, May 3, 2013

Jalan Pintas untuk Menangani Perintah-Perintah JQuery di dalam Peraturan Kondisi

Dalam sebuah forum Saya pernah sekali diajari mengenai cara tersingkat untuk menangani perintah-perintah jQuery di dalam peraturan kondisi yang biasa dituliskan seperti ini:

if (statement) {
    $(selector).method1();
} else {
    $(selector).method2();
}

Kode di bawah ini adalah jalan pintas untuk kondisional di atas:

$(selector)[statement ? "method1" : "method2"]();

Pada awalnya Saya merasa bingung dengan sudut pandang yang orang tersebut berikan, sampai kemudian Saya menyadari bahwa pada dasarnya ini hanyalah sebuah jalan pintas peraturan if/else untuk memanggil item objek yang berbeda berdasarkan kondisi tertentu.

jQuery, pada dasarnya hanyalah sekumpulan fungsi yang dinyatakan di dalam objek. Kurang lebihnya seperti ini (memang tidak sama persis seperti ini, tapi setidaknya lumayan mirip):

var foo = {
    addClass: function(param) { ... },
    removeClass: function(param) { ... },
    toggleClass: function(param) { ... },
    hasClass: function(param) { ... }
    ...
}

Sehingga setiap fungsi bisa dipanggil dengan cara seperti ini:

foo.addClass('bar');
foo.removeClass('bar');

Atau seperti ini:

foo["addClass"]('bar');
foo["removeClass"]('bar');

Katakanlah kita ingin memanggil fungsi tertentu di dalam foo hanya jika suatu kondisi terpenuhi. Jika kondisi tidak terpenuhi, maka fungsi cadangan yang lain akan dijalankan:

if (abc === true) {
    foo["removeClass"]('bar');
} else {
    foo["addClass"]('bar');
}

Yang mana jika diubah ke pernyataan kondisional ringkas akan menjadi seperti ini:

var x = (abc === true) ? "removeClass" : "addClass";
foo[x]('bar');

Sehingga kita bisa menyimpulkannya menjadi seperti ini:

foo[abc === true ? "removeClass" : "addClass"]('bar');

Contoh Penerapan

Berikut ini adalah contoh penerapan kondisional jalan pintas untuk menangani perintah .slideDown() dan .fadeOut() berdasarkan visibilitas panel:

Sebelum

$('h2').click(function() {
    $(this).toggleClass('active');
    if ($(this).next().is(':hidden')) {
        $(this).next().slideDown();
    } else {
        $(this).next().fadeOut();
    }
});

Sesudah

$('h2').click(function() {
    $(this).toggleClass('active')
        .next()[$(this).next().is(':hidden') ? "slideDown" : "fadeOut"]();
});

Lihat Demo

Labels: , , ,

Monday, April 22, 2013

Mengaktifkan Fitur Komentar Google+ (Untuk yang Gagal)

Baru-baru ini Blogger telah merilis fitur komentar baru yang memungkinkan kita untuk mengintegrasikan komentar Blogger ke Google+. Untuk mengaktifkannya mudah. Masuk saja ke tab Google+ kemudian centang pilihan “Gunakan Google+ Komentar di blog ini”:

Beralih ke sistem komentar Google+
Berpindah ke sistem komentar Google+

Jika template blog yang digunakan masih standar, seharusnya area komentar lama akan langsung digantikan dengan komentar baru dari Google+. Tapi jika template yang dipakai sudah tidak standar lagi, kita masih bisa menggunakan cara manual untuk menampilkan komentar Google+. Berikut ini adalah langkah-langkahnya:

Pertama-tama masuk ke halaman editor HTML template kemudian cari kode ini (loncat saja ke widget Blog1, kemudian jabarkan):

<b:includable id='comments' var='post'>

Jangan lupa untuk menyimpan salinan template ke komputer sebelum melakukan modifikasi.

Setelah ditemukan, salin kode di bawah ini kemudian letakkan di atasnya:

<b:includable id='g-comments' var='post'>

  <div id='g-comments-outer'>
    <div class='g-comments' expr:data-href='data:blog.canonicalUrl' data-width='600' data-first_party_property='BLOGGER' data-view_type='FILTERED_POSTMOD'>
      <a href='#g-comments-outer' id='g-comments-loader'>Add Comments</a>
    </div>
  </div>

  <!-- For slow connection -->
  <script>
  //<![CDATA[
  (function() {
    var btn = document.getElementById('g-comments-loader'),
    showGC = function() {
      var script = document.createElement('script'),
        head = document.getElementsByTagName('head')[0], old;
      script.type = "text/javascript";
      script.id = "g-comments-script";
      script.src = "//apis.google.com/js/plusone.js";
      if (document.getElementById('g-comments-script')) {
        old = document.getElementById('g-comments-script');
        old.parentNode.removeChild(old);
      }
      head.appendChild(script);
    };
    btn.onclick = function() {
      this.innerHTML = "Memuat...";
      showGC();
      return false;
    };
  })();
  //]]>
  </script>

</b:includable>

Kode 600 merupakan variabel untuk menentukan lebar area komentar. Sesuaikan dengan lebar area posting blog Anda. Jika perlu ganti juga label Add Comments dengan kata-kata yang lain.

Setelah itu cari kode ini kemudian jabarkan:

<b:includable id='main' var='top'>

Temukan kode yang kurang lebih tampak seperti ini:

<b:include data='post' name='post'/>
<b:if cond='data:blog.pageType == &quot;static_page&quot;'>
  <b:include data='post' name='comment_picker'/>
</b:if>
<b:if cond='data:blog.pageType == &quot;item&quot;'>
  <b:include data='post' name='comment_picker'/>
</b:if>

Ganti dengan kode ini:

<b:include data='post' name='post'/>
<b:if cond='data:blog.pageType == &quot;static_page&quot;'>
  <!-- b:include data='post' name='comment_picker'/ -->
  <b:include data='post' name='g-comments'/>
</b:if>
<b:if cond='data:blog.pageType == &quot;item&quot;'>
  <!-- b:include data='post' name='comment_picker'/ -->
  <b:include data='post' name='g-comments'/>
</b:if>

Atau jika tidak ada kode yang tampak seperti di atas, mungkin karena bentuknya masih seperti ini:

<b:include data='post' name='post'/>
<b:if cond='data:blog.pageType == &quot;static_page&quot;'>
  <b:include data='post' name='comments'/>
</b:if>
<b:if cond='data:blog.pageType == &quot;item&quot;'>
  <b:include data='post' name='comments'/>
</b:if>

Ganti dengan kode ini, jika yang ditemukan adalah kode dengan pola yang ke dua:

<b:include data='post' name='post'/>
<b:if cond='data:blog.pageType == &quot;static_page&quot;'>
  <!-- b:include data='post' name='comments'/ -->
  <b:include data='post' name='g-comments'/>
</b:if>
<b:if cond='data:blog.pageType == &quot;item&quot;'>
  <!-- b:include data='post' name='comments'/ -->
  <b:include data='post' name='g-comments'/>
</b:if>

Klik Simpan Template. Seharusnya sekarang komentar Google+ sudah tampil di bawah posting.

Langkah Terakhir

Langkah terakhir adalah menghilangkan tanda centang pada pilihan “Gunakan Google+ Komentar di blog ini” di tab Google+. Mengapa? Karena fitur komentar Google+ yang kita buat sekarang sudah ditampilkan dengan cara manual. Dengan cara menampilkan komentar Google+ secara manual, maka ini memungkinkan kita untuk melakukan modifikasi area komentar dengan lebih leluasa karena markup HTML yang dibuat telah disisipkan secara manual juga. Sehingga —sebenarnya— kita bisa menampilkan komentar Google+ ini di area manapun yang kita suka, yaitu berdasarkan letak elemen #g-comments-outer.

Suatu saat mungkin Anda ingin membuat tampilan komentar ini menjadi berbentuk seperti jendela munculan.

Tapi jujur saja, secara pribadi Saya tidak begitu tertarik untuk mengaktifkan fitur ini, sama halnya dengan ketidaktertarikan Saya untuk menyisipkan komentar Facebook di dalam blog Blogger. Jika Saya bisa menggunakan fitur bawaan yang ada, mengapa Saya harus menggunakan pihak ke tiga? Tapi ini cuma pendapat. Jika Anda berminat untuk berpindah ke sistem komentar Google+, ikuti saja langkah-langkah di atas. Lagipula komentar yang masuk akan terintegrasi secara langsung dengan komentar Blogger, sehingga jika suatu saat Anda ingin kembali untuk menggunakan fitur komentar bawaan dari blog, maka komentar-komentar yang sebelumnya telah terbit ke Google+ akan tampil kembali di komentar Blogger.

Seharusnya sih begitu.

Labels: , ,