DTE :]

Saturday, March 28, 2020

Cara Merilis Paket Node.js Secara Otomatis Bersamaan dengan Rilis pada GitHub

GitHub + NPM
Gambar oleh GitHub.

Dengan membaca artikel ini, Saya menganggap kamu sudah paham mengenai apa dan bagaimana itu NPM dan GitHub.

Pada tanggal 16 Maret 2020 yang lalu, GitHub —yang sebelumnya juga telah berhasil diakuisisi oleh Microsoft— mengumumkan sebuah kabar gembira bahwa NPM memutuskan untuk bergabung dengan GitHub. Dengan mengesampingkan ketidaksukaan Saya terhadap pola-pola modul JavaScript, sejak saat itu Saya mulai menelusuri tentang bagaimana cara melakukan integrasi NPM dengan GitHub. Untuk apa? Untuk membuat aktivitas NPM menjadi serba otomatis menyesuaikan dengan berbagai aktivitas yang Saya lakukan di GitHub.

Karena Saya yang tidak suka dengan NPM namun tidak bisa menghindari kenyataan bahwa beberapa pengguna aplikasi Saya ternyata menginginkan Saya untuk memberikan opsi pemasangan aplikasi melalui NPM, dan karena aktivitas utama koding jarak jauh Saya sekarang hampir seluruhnya berada di GitHub, dan karena dalam lubuk hati Saya yang paling dalam (cie…) sebenarnya Saya tetap saja tidak menyukai konsep pemuatan modul di NPM, pada akhirnya Saya memutuskan untuk keluar dari zona nyaman Saya.

Sebelumnya Saya sudah beberapa kali melakukan rilis paket Node.js secara manual, secara biasa, sesuai dengan prosedur pada situs web dokumentasi NPM, yaitu dengan cara menuliskan perintah npm publish --access public setiap kali Saya sudah siap merilis versi paket yang lebih baru, yang hal tersebut tentu saja memaksa Saya untuk memasang lingkungan Node.js terlebih dahulu di komputer yang sedang Saya pakai, yang kadang hal tersebut menurut Saya agak ribet, karena saat ini Saya masih berada di luar kebutuhan untuk menggunakan Node.js sebagai peladen lokal.

Kemudian Saya mengetahui bahwa NPM sekarang bergabung dengan GitHub. Saya pikir, pasti ada cara tertentu untuk membuat semuanya menjadi serba otomatis. Cara seperti itu pasti ada, dan ternyata memang benar.

Mengenal GitHub Actions

Actions atau Aksi pada GitHub merupakan fitur yang memungkinkan kita untuk mengeksekusi perintah-perintah tertentu secara otomatis setiap kali kita melakukan aktivitas pada GitHub. Cara kerjanya sama dengan kait atau hook pada sistem manajemen konten Mecha dan pendengar acara atau event listener pada JavaScript. Setiap kali kita melakukan aktivitas dorong dan tarik, menulis isu dan komentar misalnya, pada saat itu sebenarnya terdapat kait dengan nama tertentu yang akan dieksekusi oleh GitHub. Dengan memanfaatkan nama kait tersebut, maka kita bisa mengeksekusi perintah yang kita inginkan pada saat kait tersebut dipanggil.

Pada kasus yang sedang kita bahas sekarang, nama kait yang Saya maksud di sini adalah release, dan bentuk aksi yang akan kita lakukan adalah mengeksekusi perintah npm publish pada repositori jarak jauh yang baru saja kita rilis. Namun karena kita tidak memiliki akses kepada peladen Node.js di GitHub, dan karena membuat kait web memerlukan peladen Node.js pribadi untuk menentukan tautan sebagai penerima muatan dari GitHub, maka memanfaatkan fitur Webhooks menjadi tidak mungkin.

Fitur aksi pada GitHub memberikan akses kepada kita untuk memanfaatkan lingkungan peladen yang ada di GitHub dengan cara menuliskan berkas konfigurasi tertentu untuk dijalankan oleh GitHub.

Menu Actions seharusnya sudah ada pada halaman awal repositori masing-masing selama fitur tersebut tidak dinonaktifkan melalui menu Settings. Untuk membuat aksi, klik menu Actions pada repositori yang diinginkan kemudian pilih jenis alur kerja yang akan dipakai, dalam hal ini adalah Node.js:

Memilih alur kerja.
Memilih alur kerja.

Kamu mungkin juga akan melihat opsi Publish Node.js Package di situ yang secara logika seharusnya lebih tepat untuk dipilih. Tapi di sini Saya lebih cenderung ke opsi Node.js karena kedua opsi tersebut pada dasarnya sama. Hanya saja opsi Node.js sifatnya lebih menyeluruh, sedangkan opsi Publish Node.js Package tersedia hanya untuk menangani aktivitas publikasi paket saja. Harapan Saya adalah ketika suatu saat nanti Saya ingin membuat artikel yang sejenis atau terkait dengan implementasi GitHub Actions pada artikel ini, maka Saya hanya tinggal meminta kalian untuk melakukan sedikit perubahan pada pengaturan yang sudah kalian buat di sini.

Setelah mengambil pilihan, kamu akan diminta untuk mengisi formulir yang bertujuan untuk membuat berkas bernama .\.github\workflows\nodejs.yml seperti ini:

Membuat `nodejs.yml`
Membuat berkas konfigurasi.

Hapus semua isinya kemudian ganti dengan ini:

name: Publish

on:
  release:
    types: [published]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - uses: actions/setup-node@v1
        with:
          node-version: 12
          registry-url: https://registry.npmjs.org/
      - run: yarn install
      - run: npm publish --access public
        env:
          NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}

Klik tombol Start commit untuk memulai pembuatan berkas.

Kalau kamu sudah mahir menggunakan aplikasi Git, kamu sebenarnya bisa kok membuat berkas tersebut secara manual pada repositori lokal melalui terminal. Setelah itu, kamu tinggal menjalankan perintah git push untuk menambahkan berkas tersebut ke repositori jarak jauh. Namun karena pada contoh di atas, yang kamu lakukan adalah membuat berkas baru secara langsung melalui repositori jarak jauh, maka kamu perlu melakukan sinkronisasi kembali dengan repositori lokal, dengan cara menjalankan perintah git pull pada terminal untuk memastikan agar kedua repositori tetap berada dalam kondisi dan status yang sama persis.

Pada data konfigurasi di atas, kita bisa sedikit memahami, dimulai dari data yang ini, yang berfungsi untuk menjalankan jobs pada saat kait release dengan tipe published berhasil dipanggil:

on:
  release:
    types: [published]

Perintah publikasi paket berada pada baris ini:

jobs:
  build:
    steps:
      - …
      - …
      - run: npm publish --access public

Variabel ${{secrets.NPM_TOKEN}} berisi token rahasia yang perlu kita buat secara manual melalui dasbor NPM.

Membuat Token Akses untuk Autentikasi

Agar bisa berkomunikasi dengan NPM, kita perlu membuat token rahasia yang berfungsi untuk memberikan akses kepada paket-paket kita di NPM.

Buka tautan https://www.npmjs.com/settings/nama-pengguna/tokens atau klik pada ikon avatar di pojok kanan atas kemudian pilih menu Auth Tokens. Klik pada tombol Create New Token.

Membuat token akses di NPM.
Membuat token akses baru.

Pilih tingkatan akses ke Read and Publish kemudian klik tombol Create Token.

Segera salin token yang sudah berhasil dibuat!

Sampai di sini, sebenarnya kamu bisa mengganti variabel ${{secrets.NPM_TOKEN}} dengan kode token yang baru saja kamu salin. Tapi karena token ini bersifat rahasia dan berfungsi sebagaimana kata kunci yang kita gunakan untuk log masuk, maka ada baiknya jika kita simpan token tersebut ke dalam variabel secrets.NPM_TOKEN di GitHub, sehingga nilainya dapat diakses melalui properti NPM_TOKEN di variabel secrets, namun tidak akan bisa dilihat secara langsung karena sudah tersimpan di tempat yang lain.

Menyimpan Token NPM di GitHub

Untuk menyimpan token rahasia di GitHub, gunakan fitur GitHub Secrets. Pada setiap repositori, fitur ini dapat diakses melalui menu Settings. Buat variabel baru dengan nama NPM_TOKEN kemudian tempelkan kode token yang sudah kamu salin tadi:

Membuat token akses di NPM.
Menyimpan token akses rahasia ke GitHub.

Klik tombol Add secret. Kode token rahasia sudah berhasil disimpan dan tidak akan pernah bisa dibuka lagi. Kamu tidak perlu melakukan perubahan apa-apa pada berkas nodejs.yml yang telah kamu buat sebelumnya.

Sistem rilis paket Node.js otomatis sudah selesai dibuat!


Cara Merilis Paket Node.js Melalui GitHub

Untuk merilis paket dari GitHub ke NPM secara otomatis, kamu hanya perlu membuat item rilis baru di GitHub untuk memicu kait release pada repositori tersebut. Tapi sebelum itu, pastikan properti version di dalam berkas package.json sudah diperbarui:

{
  "name": "@taufik-nurrohman/query-string-parser",
  "description": "Convert URL’s query string into JavaScript object.",
  "version": "1.0.1",
  "main": "q2o.js",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/taufik-nurrohman/query-string-parser.git"
  },
  "author": "Taufik Nurrohman",
  "license": "MIT",
  …
}

Pada halaman formulir rilis baru, tentukan nama label sesuai dengan nilai properti version pada berkas package.json:

Merilis versi baru.
Merilis versi baru.

Nama label ini, setahu Saya tidak ada kaitannya dengan NPM karena NPM hanya akan membaca berkas package.json pada repositori terkait untuk mengecek apakah nilai properti version sudah berubah atau belum. Tapi ada baiknya untuk tetap menyesuaikan nilainya dengan versi paket untuk menjaga konsistensi antara GitHub dengan NPM. Silakan isi judul dan konten rilis sesuka hati untuk memperjelas maksud dan tujuan rilis pada versi tersebut.

Setelah berhasil melakukan rilis pada GitHub, harusnya salinan repositori GitHub tersebut akan secara otomatis dibuat (atau diperbarui jika sudah ada) sebagai paket Node.js di situs web NPM, seperti ini:

Beberapa detik yang lalu.
Lihat keterangan bahwa paket @taufik-nurrohman/query-string-parser baru saja diperbarui beberapa detik yang lalu.

Labels: ,

Saturday, October 5, 2019

Tema Open-Source untuk Blogger

Open-Source Blogger Theme
Tema Blogger Dasar: BloggerPack.

Sangat sulit mencari komunitas pengembang tema Blogger yang memiliki semangat keterbukaan kode sumber seperti Saya. Selain karena Blogger bukan merupakan layanan blog yang terbuka, minimnya dokumentasi resmi akan kode-kode pada tema Blogger memaksa kita untuk menggunakan waktu dan tenaga yang banyak untuk mempelajari setiap kode yang ada secara otodidak dan tanpa bimbingan. Akibatnya, orang-orang yang berhasil tahu dan/atau menemukan sesuatu yang baru pada mesin Blogger lebih memilih untuk mengembangkan dan merilis tema-tema mereka secara mandiri dan bahkan sembunyi-sembunyi tanpa memberitahu kita tentang apa-apa saja yang telah mereka lakukan dan kembangkan pada tema yang mereka buat.

Topik-topik seperti cara menandai komentar administrator, cara menghapus CSS dan JavaScript bawaan Blogger, cara membuat komentar berbalas, dan cara menyisipkan kode CSS dan JavaScript kustom pada postingan tertentu pernah begitu populer sepanjang pengamatan Saya dari pertama kali Saya mengenal Blogger sampai sekarang.

Berat rasanya untuk membagikan temuan-temuan tersebut secara cuma-cuma karena kebanyakan dari kita selama ini memang hanya bisa menebak-nebak saja pada apa yang terjadi pada kode-kode XML Blogger dari sisi peladen dengan melihat melalui kode keluaran HTML-nya saja. Sangat memprihatinkan memang, tapi Saya bisa memaklumi hal tersebut karena dahulu Saya juga pernah menjadi seperti kalian.

Rasanya seperti sedang mencari harta karun. Ketika kalian berhasil menemukannya, maka kalian akan terburu-buru memberitahukannya kepada semua orang di seluruh alam semesta karena kalian takut nanti akan ada orang lain juga yang berhasil menemukannya setelah kalian namun lebih dahulu memberitahukannya. Ada semacam ambisi untuk menjadi yang pertama, dan temuan-temuan sederhana pada Blogger yang serba closed-source membuat kalian seolah layak untuk mendapatkan hak paten dari Pemerintah, sehingga orang lain yang ingin mengimplementasikan temuan kalian akan memiliki kewajiban untuk membayar royalti ratusan juta rupiah kepada kalian. Kalian kemudian akan menjadi orang yang kaya raya berkat Blogger.

Semangat-semangat semacam itulah yang kadang membuat para pengembang Blogger menjadi tertantang. Sampai sekarang Saya bahkan masih belum tahu tentang bagaimana cara mendeteksi kueri ?amp=1 pada URL blog.

Sudah cukup mengkhayalnya ya! Sekarang Saya mau memperkenalkan sebuah proyek tema Blogger dengan kode sumber yang terbuka dari Hermawan Yogi, namanya BloggerPack. Tema ini adalah tema mentah yang tidak bisa digunakan secara langsung tanpa dilakukan kompilasi dan pembangunan terlebih dahulu menggunakan alat pembangun (beliau menggunakan Grunt).

Dokumentasi tingkat lanjut dapat dipelajari pada halaman proyek. Di sini Saya hanya ingin menjelaskan garis besarnya saja untuk kalangan awam (para pengguna), sekedar untuk membantu memahami struktur kode sumber tema yang ada.

Struktur Sumber

Berikut ini adalah struktur berkas dan folder kode-kode mentah tema sebelum dilakukan kompilasi. Semua kode tersimpan di dalam folder src sebagai komponen-komponen yang terpisah:

.\
└── src\
    ├── _defaultmarkups\
    ├── _js\
    ├── _plugins\
    ├── _scss\
    ├── _views\
    ├── config.json
    ├── …
    └── theme.xml

Berkas-berkas tersebut tidak bisa digunakan secara langsung karena setiap komponen masih terpisah-pisah dan dibuat dengan sintaks kode pra-produksi seperti sintaks SCSS untuk membentuk kode CSS dan sintaks modul ES6 untuk membentuk kode JavaScript versi peramban.

Tujuan kita memisahkan berkas-berkas tema sebagai komponen-komponen individu adalah untuk memudahkan para pengembang dalam menambahkan dan/atau mengurangi fitur tema yang ada. Tapi mungkin itu hanya akan memudahkan dari sisi pengembang saja, sedangkan dari sisi pengguna biasanya akan berpendapat kalau hal-hal tersebut dirasa terlalu rumit.

Pengguna pada dasarnya hanya ingin tahu di sebelah mana dia bisa menemukan tombol mengunduh tema dan bagaimana cara memasang tema tersebut. Pengguna tidak mau tahu seluk-beluk proses pengembangannya secara rinci, mereka hanya peduli pada hasil akhirnya saja dan oleh karena itu penjelasan-penjelasan Saya pada beberapa paragraf di bawah mungkin tidak akan berguna bagi Anda yang hanya berniat untuk segera menggunakan tema. Silakan langsung meloncat pada penjelasan mengenai folder dist.

Konfigurasi

Kita menggunakan Grunt untuk menggabungkan berkas-berkas pada folder src. Berkas-berkas tersebut nantinya akan dibentuk menjadi beberapa berkas baru yang siap pakai (biasanya sudah dalam bentuk kode yang sudah dikompres dan digabung menjadi satu, terutama pada kode CSS dan JavaScript tema). Berkas-berkas tersebut nantinya akan tersimpan secara otomatis pada folder dist.

Sebelum menjalankan perintah ‘bangun’, terlebih dahulu Anda buka berkas config.json untuk melakukan konfigurasi tema, terutama pada bagian atribusi, versi dan waktu perilisan. Cantumkan nama tema dan nama pembuatnya sesuai kehendak (di bawah lisensi MIT):

{
  "theme": {
    "name": "Tema Pribadi",
    "version": "2.0.0",
    "date": "5 Oktober 2019",
    "homepage": "https://dte-feed.blogspot.com",
    "author": {
      "name": "Taufik Nurrohman",
      "url": "https://github.com/tovic"
    },
    …
    …
  }
}

Setelah selesai, jalankan perintah $ grunt. Saya tidak akan menjelaskannya secara rinci di sini. Silakan pelajari sendiri pada dokumentasi tema dan situs web resmi Grunt bagi Anda yang ingin serius mengembangkan tema ini.

Struktur Produk

Berikut ini adalah struktur berkas dan folder kode-kode tema setelah dilakukan kompilasi dan pembangunan. Semua kode tersimpan di dalam folder dist:

.\
└── dist\
    ├── css\
    ├── js\
    ├── …
    └── theme.xml

Di situ terdapat berkas bernama theme.xml yang dapat kalian pasang ke Blogger. Folder css dan js berisi hasil akhir dari kompilasi dan pembangunan kode-kode SCSS dan modul ES6 pada folder src. Berkas-berkas tersebut pada dasarnya tidak ada gunanya karena kode-kode CSS dan JavaScript yang sama juga sudah tergabung ke dalam berkas theme.xml secara inline. Berkas-berkas CSS dan JavaScript yang terpisah tersebut hanya akan berguna ketika kalian berniat untuk menyimpan mereka ke ruang penyimpanan web tertentu di luar Blogger, untuk kemudian dapat dimuat melalui tag <link href="./jalur/ke/main.min.css" rel="stylesheet"> dan <script src="./jalur/ke/main.min.js"></script>.

Dokumentasi dan tutorial bawaan masih belum sepenuhnya selesai. Kalian bisa memberikan kontribusi dengan cara membuat permintaan dorong atau membuat isu baru di GitHub.

Labels: , ,

Saturday, July 13, 2019

PHP 7.4.x, dan Kebiasaan-Kebiasaan yang Perlu Anda Ubah

Logo PHP
Logo PHP.

Selalu ada hal-hal yang menarik untuk diceritakan dari sejarah bahasa pemrograman, salah satunya adalah bahasa pemrograman PHP yang hingga saat ini telah digunakan oleh 79% dari situs web di seluruh dunia. Terdapat berbagai kritik negatif mengenai keberadaannya, beberapa di antaranya adalah karena inkonsistensi hasil yang diberikan ketika sebuah data dibandingkan atau disatukan dengan data yang lain, juga mengenai keputusan penamaan fungsi yang sangat tidak terpola. Tapi di luar dari semua itu, Rasmus Lerdorf sendiri mengatakan bahwa beliau tidak pernah merencanakan PHP. Semuanya terjadi begitu saja.

“Well, I didn’t plan PHP. I think in terms of solving problems, not in terms of software projects. I actually hate programming, but I love solving problems.”

Dengan proyek pengembangan perangkat lunak yang tidak terencana seperti itu tentu saja akan membawa kita pada kebiasaan-kebiasaan buruk yang terbentuk bukan karena kita yang tidak disiplin akan tetapi karena tidak adanya aturan yang jelas mengenai kaidah-kaidah pemrograman. Tidak ada aturan yang pasti apakah 123 == '123x' akan memberikan hasil yang sama dengan '123' == '123x'. Bahasa sederhananya: PHP itu terlalu bebas!

Sejak rilis PHP 7.0.0 hingga sekarang, telah begitu banyak perubahan yang terjadi di dalam ekosistem PHP. Dan Saya akui kondisinya makin membaik. Kecepatan eksekusinya meningkat drastis. Masih belum bisa dikatakan sempurna memang, namun sampai sekarang fitur-fitur baru dan perbaikan-perbaikan masalah di sana-sini masih terus dilakukan. Melalui artikel ini, Saya ingin membahas beberapa hal terkait dengan cara Anda menulis kode PHP di masa lalu. Karena PHP versi 7.0.0 sudah tidak sama lagi dengan PHP versi yang dulu. Meskipun kode lama Anda masih dapat bekerja di versi PHP yang baru, akan tetapi tidak ada salahnya jika Anda mengikuti beberapa saran Saya untuk mulai membiasakan diri menulis kode PHP dengan lebih baik lagi karena jujur saja Saya tidak suka membaca kode-kode PHP yang kalian tulis!

Gunakan Sintaks Jajaran Pendek

Fitur ini sebenarnya sudah ada sejak PHP 5.4.0 bersamaan dengan hadirnya fitur Trait. Selain dapat meringkas kode dan memperkecil ukuran berkas, sintaks ini juga akan sangat serasi ketika dipadukan dengan perintah peletakan dan pengambilan data jajaran:

$array = ['A', 'B', 0, true];

$array[0] = 'Z';

echo $array[0];

Nilai Alternatif

Dahulu kita menggunakan cara-cara seperti ini untuk menentukan nilai alternatif ketika data yang diminta tidak ada:

$var = isset($array['var']) ? $array['var'] : 1;
$var = array_key_exists('var', $array) ? $array['var'] : 1;

Namun sejak kemunculan operator penggabungan null pada PHP 7.0.0, sekarang kita bisa dengan sangat mudah membuat deretan nilai alternatif tanpa harus melalui perintah isset berkali-kali:

$var = $array['var'] ?? 1;
$var = $foo['var'] ?? $bar['var'] ?? $baz ?? 1;

Argumen Fungsi

func_get_arg(), func_get_args(), dan func_num_args() adalah fungsi-fungsi yang paling sering kita gunakan untuk mendapatkan data argumen dari sebuah fungsi. Cara penggunaannya adalah seperti berikut:

function image($image, $x = null, $y = null, $w = null, $h = null) {
    // `image('.\path\to\image.jpg', $w, $h)`
    if (func_num_args() < 4) {
        $w = $x;
        $h = $y;
        return resize($image, $w, $h);
    }
    // `image('.\path\to\image.jpg', $x, $y, $w, $h)`
    return crop($image, $x, $y, $w, $h);
}

Perhitungan-perhitungan argumen fungsi seperti ini biasanya dibuat untuk mempermudah pengguna dalam menggunakan fungsi utama, yaitu dengan cara mengizinkan mereka untuk melompati beberapa variabel yang sifatnya opsional. Contoh lain dapat Anda temukan juga pada cara kerja fungsi substr().

Beberapa orang menganggap fitur-fitur tersebut (fungsi func_get_arg(), func_get_args(), dan func_num_args()) sebagai fitur yang buruk dan tidak logis karena fungsi tidak seharusnya memiliki akses kepada pengurai sintaks PHP itu sendiri. Kelemahan ini juga tampak pada fungsi get_called_class() dan get_parent_class(), yang sekarang sudah mengalami perkembangan yang lebih baik dengan hadirnya konstanta *::class pada PHP 5.5, sehingga kita tidak perlu lagi menggunakan fitur-fitur tersebut.

Untuk membuat peraturan kondisi berdasarkan argumen fungsi menjadi tampak lebih nyaman untuk dibaca, kita bisa menggunakan fitur pembongkar argumen yang hadir pada PHP 5.6:

function image(...$args) {
    // `image('.\path\to\image.jpg', $w, $h)`
    if (count($args) < 4) {
        return resize(...$args);
    }
    // `image('.\path\to\image.jpg', $x, $y, $w, $h)`
    return crop(...$args);
}

Dan untuk mendapatkan nama kelas yang sedang dieksekusi, kita bisa menggunakan konstanta *::class atau __CLASS__ seperti ini:

class Foo {

    public function getClassName1() {
        return static::class;
    }

    public function getClassName2() {
        return __CLASS__;
    }

}

Oya! Sebagai informasi tambahan saja, pada JavaScript kita bisa menuliskan arguments begitu saja untuk mendapatkan daftar argumen fungsi yang ada. Jadi, di sini arguments berlaku sebagai token keyword yang bekerja sebagaimana token if, var, function, dan kawan-kawan:

function image() {
    if (arguments.length < 4) {
        return resize.apply({}, arguments);
    }
    return crop.apply({}, arguments);
}

Fitur Pembongkaran dan Pengepakan Argumen

Kasus ini hampir sama dengan kasus sebelumnya, hanya saja di sini Saya lebih cenderung kepada fitur sebaliknya yaitu fitur pengepakan argumen. Dengan fitur ini, kita tidak perlu lagi menggunakan fungsi-fungsi yang biasanya memiliki pola penamaan *_array seperti call_user_func_array() dan preg_replace_callback_array(). Karena kita bisa meletakkan data jajaran sebagai deret argumen fungsi dengan cara seperti ini:

$args = ['A', 0, true];

call_user_func('foo', ...$args); // Sama dengan `call_user_func('foo', $args[0], $args[1], $args[2])`

Operator Sebar pada Jajaran

Fitur ini dapat digunakan untuk mengeliminasi fungsi array_merge(). Cara penggunaannya adalah seperti ini:

$array = ['A', 'B', 'C'];

return [0, 1, 2, ...$array]; // `[0, 1, 2, 'A', 'B', 'C']`

Selain dari itu, fitur ini juga memiliki satu kelebihan lagi yaitu dapat diletakkan di antara data jajaran. Jadi tidak harus berada di akhir jajaran:

return [0, 1, 2, ...$array, true]; // `[0, 1, 2, 'A', 'B', 'C', true]`

Perintah-perintah yang kompatibel untuk digunakan pada PHP dengan versi yang lebih lama adalah sebagai berikut:

return array_merge([0, 1, 2], $array);

return [0, 1, 2] + $array;
$a = [0, 1, 2, true];
$b = array_pop($a);

return array_merge($a, $array, [$b]);

Deklarasi Tipe pada Argumen dan Pengembalian Fungsi

Dengan menggunakan fitur ini, selain dapat menampilkan pesan kesalahan ketika masukan data yang diberikan ke dalam argumen fungsi tidak sesuai dengan jenis data yang diinginkan, fitur ini juga dapat mempermudah pembaca kode sumber untuk memahami apa maksud dan penggunaan fungsi tersebut tanpa harus membaca dokumentasi secara lengkap:

interface Get {
    public function chunk(array $data, int $i): array {}
    public function files(string $path, string $x): Pages {}
    public function __toString(): string {}
}

Pada bagian __toString() sebenarnya agak berlebihan. Kode di atas hanya sebagai contoh saja.

Konversi Penamaan

Ada baiknya untuk mengikuti kaidah-kaidah tertentu dalam menuliskan nama konstanta, variabel, kelas dan fungsi seperti bagaimana cara memisahkan kata-kata pada deret nama, apakah dengan membedakan besar dan kecilnya huruf, atau dengan menggunakan karakter garis bawah seperti ini:

$foo;
$foobar;
$foobarbaz;
$foo;
$fooBar;
$fooBarBaz;
$Foo;
$FooBar;
$FooBarBaz;
$foo;
$foo_bar;
$foo_bar_baz;

Kita juga perlu menentukan bagaimana urutan prioritas pada masing-masing sub-nama. Apakah ingin mengaturnya dari kiri ke kanan untuk menciptakan kesan hirarki, atau dengan cara mengaturnya dari kanan ke kiri untuk menciptakan kesan kalimat yang dapat dibaca secara alami:

function button(): string {}
function button_primary(): string {}
function button_primary_large(): string {}
function button(): string {}
function buttonPrimary(): string {}
function buttonPrimaryLarge(): string {}
namespace {
    function button(): string {}
}

namespace button {
    function primary(): string {}
}

namespace button\primary {
    function large(): string {}
}
function button(): string {}
function primaryButton(): string {}
function largePrimaryButton(): string {}

Pastikan untuk mengikuti pola-pola tersebut secara konsisten hingga proyek Anda selesai sehingga kode Anda akan lebih mudah untuk diurus di kemudian hari. Pola yang konsisten juga dapat mempermudah Anda dan orang lain untuk melakukan pencarian nama konstanta, variabel, kelas dan fungsi tertentu melalui perintah Find & Replace karena konsep penamaan yang sudah terpola.

Hemat dalam Menggunakan Variabel

Dahulu kita tidak bisa mengambil satu item dari string yang dipisahkan oleh fungsi explode() secara langsung karena cara kerja pengurai token PHP yang masih sangat terbatas. Jadilah kita menggunakan ekstra variabel untuk mengatasi masalah tersebut:

$tags = explode(', ', 'Foo, Bar, Baz');

echo $tags[0];

Tapi masalah tersebut sudah berhasil diatasi sejak PHP versi 5.4, sehingga kita bisa mengambil item tertentu secara langsung dari pengembalian fungsi explode() seperti ini:

echo explode(', ', 'Foo, Bar, Baz')[0];

Ringkas juga pemanggilan metode-metode kelas atau data-data dari pengembalian fungsi jika memang memungkinkan dan tidak merusak keterbacaan:

echo (new Page('.\path\to\page.md'))->get('title');
echo q2o('?a=b&c=d&e=f')['c'] ?? false;

Keterbacaan PHP di dalam Berkas HTML

Usahakan untuk menghindari kebiasaan mencampurkan antara kode PHP sebagai program dengan kode PHP sebagai bagian dari kode HTML seperti ini:

<h2>
  <?php if ($page->link) { ?>
  <a href="<?php echo $page->link; ?>" target="_blank">
    <?php echo $page->title; ?>
  </a>
  <?php } else if ($page->url) { ?>
  <a href="<?php echo $page->url; ?>">
    <?php echo $page->title; ?>
  </a>
  <?php } else { ?>
  <span>
    <?php echo $page->title; ?>
  </span>
  <?php } ?>
</h2>

PHP pada dasarnya memiliki peran ganda, selain sebagai scripting language juga sebagai templating language, yang berarti bahwa kode PHP dapat disatukan dengan kode HTML. Jika yang ingin Anda tuliskan adalah kode untuk menampilkan data pada HTML, maka sintaks alternatif berikut ini akan lebih nyaman untuk dibaca, terutama oleh orang-orang yang ingin membuka berkas tersebut melalui editor kode berwarna:

<h2>
  <?php if ($page->link): ?>
  <a href="<?php echo $page->link; ?>" target="_blank">
    <?php echo $page->title; ?>
  </a>
  <?php elseif ($page->url): ?>
  <a href="<?php echo $page->url; ?>">
    <?php echo $page->title; ?>
  </a>
  <?php else: ?>
  <span>
    <?php echo $page->title; ?>
  </span>
  <?php endif; ?>
</h2>

Jangan juga mencampurkan kode PHP sebagai program dengan kode HTML mentah seperti ini, ini sangat kacau!

<?php

class Article {

    public function getHTML(string $source) {
        $data = fetch($source)->then(function($response) {
            return $response->json;
        })->then(function($json) {

?>
<article>
<h2><?php echo $json->title; ?></h2>
<div><?php echo $json->content; ?></div>
</article>
<?php

        })->catch(function() {
?>
<article>
<h2>Error</h2>
<div>Not found.</div>
</article>
<?php

        });
    }

}

Coba gunakan echo, atau return sebagai alternatif yang lebih baik. Atau pelajari konsep MVC.

Jangan Takut Menggunakan “Magic Methods”

Metode ajaib atau magic method dalam PHP dikenal sebagai metode yang berpotensi memperlambat proses eksekusi program PHP Anda karena dalam satu kali pemanggilan metode kelas yang dilengkapi dengan metode-metode ajaib, sebuah kelas perlu untuk mencari metode yang dimaksud terlebih dahulu untuk dieksekusi sebelum pada akhirnya kelas tersebut mengeksekusi metode ajaib yang diberikan ketika metode yang dimaksud ternyata tidak ditemukan. Tapi sekarang ini arsitektur PHP sudah makin berkembang menjadi lebih baik lagi termasuk juga dari segi kecepatannya. Satu-satunya kendala mengapa PHP tidak bisa secepat bahasa-bahasa pemrograman lain adalah karena kode PHP dibaca oleh mesin secara langsung, bukannya dikompil terlebih dahulu sebelum dibaca oleh mesin. Saya sudah menggunakan metode ajaib ini pada Mecha sejak versi 2.0.0 dan justru terdapat banyak manfaat yang Saya peroleh dari fitur yang hina ini. Salah satu manfaat besar yang Saya rasakan adalah Saya jadi bisa lebih hemat dalam menuliskan metode kelas. Tanpa metode ajaib, mungkin Saya perlu membuat kelas dengan metode-metode yang tak terhitung jumlahnya seperti ini:

<?php

class Page {
    protected $data = [];
    public function __construct(array $data) {
        $this->data = $data;
    }
    public function title() {
        return $this->data['title'] ?? null;
    }
    public function description() {
        return $this->data['description'] ?? null;
    }
    public function content() {
        return $this->data['content'] ?? null;
    }
    public function tags() {
        return $this->data['tags'] ?? null;
    }
    // …
}

Dengan metode ajaib, Saya hanya perlu fokus pada data-data yang masuk ke instansi kelas tersebut saja:

<?php

class Page {
    protected $data = [];
    public function __call(string $key, array $lot = []) {
        return $this->data[$key] ?? null;
    }
    public function __construct(array $data) {
        $this->data = $data;
    }
}

Setiap orang punya pendapat dan idealisme masing-masing, dan kebetulan Saya adalah salah satu orang yang mendapatkan keuntungan dari fitur tersebut. Sehingga Saya hanya bisa menyarankan Anda untuk berpikir lebih terbuka kepada keputusan-keputusan yang telah diambil oleh para pengembang. Dan sekedar informasi tambahan saja, __construct() dan __toString() pada PHP juga termasuk metode ajaib. Dan jika Anda bisa menerima kedua hal tersebut, kenapa Anda tidak bisa mencoba untuk menerima mereka juga?

Dan sebagai informasi tambahan juga, dalam JavaScript terdapat metode sejenis bernama constructor() dan toString(), serta kelas Proxy untuk memberikan efek yang sama. Python juga punya __init__() dan __str__().

Lain-Lain

IIFE (Immediately Invoked Function Expression)

Dahulu kita memerlukan call_user_func() untuk mengeksekusi fungsi anonim:

call_user_func(function($foo, $bar, $baz) {
    // Definisikan variabel pribadi di sini…
}, 'A', 0, true);

Sekarang kita bisa menggunakan gaya IIFE seperti pada JavaScript:

(function($foo, $bar, $baz) {
    // Definisikan variabel pribadi di sini…
})('A', 0, true);

Alternatif if..elseif..else dan switch..case..default

Para pengguna PHP pada umumnya akan menggunakan cara ini:

if ($var === 'true') {
    $value = true;
} else if ($var === 'false') {
    $value = false;
} else {
    $value = null; // Bawaan
}

echo $value;
switch ($var) {
    case 'true':
        $value = true;
        break;
    case 'false':
        $value = false;
        break;
    default:
        $value = null; // Bawaan
}

echo $value;

Tapi belakangan ini Saya lebih suka menggunakan cara seperti berikut:

$value = ([
    'true' => true,
    'false' => false
])[$var] ?? null;

echo $value;

Labels: ,

Sunday, July 7, 2019

Cara Memberlakukan Kode CSS Hanya pada Tampilan Tata Letak

Warna khusus pada tampilan blok widget di halaman Tata Letak.

Cara Lama

Cara pertama ini adalah cara yang paling tua, yaitu dengan membuat selektor CSS yang diinginkan menjadi khusus untuk anak-anak elemen pada induk <body id="layout"> saja. Kekurangan dari metode ini adalah, kode CSS khusus yang seharusnya hanya dimuat pada tampilan Tata Letak akan tampil juga pada kode sumber di halaman versi publik. Kode CSS juga akan diterapkan pada halaman versi publik apabila Anda menambahkan atribut id="layout" pada elemen <body> di dalam kode XML tema:

body#layout div.section {
  background: #ff0;
  border: 4px solid #f00;
}

Cara Baru

Cara yang paling baru untuk memberlakukan kode CSS hanya pada tampilan Tata Letak adalah dengan menuliskan kode CSS di dalam elemen <b:template-skin> seperti ini:

<b:template-skin>
<![CDATA[
div.section {
  background: #ff0;
  border: 4px solid #f00;
}
]]>
</b:template-skin>

Kita mungkin bisa menggunakan tag kondisional ini sebagai cara alternatif, hanya saja Saya belum sempat mengujinya jadi Saya tidak tahu apakah cara ini bisa bekerja atau tidak. Secara logika harusnya bisa:

<b:if cond='data:view.isLayoutMode'>
<style>
div.section {
  background: #ff0;
  border: 4px solid #f00;
}
</style>
</b:if>

Anda bisa menggunakan fitur ini untuk menandai beberapa widget yang Anda anggap khusus atau penting, misalnya dengan memberikan warna yang spesial pada widget-widget tertentu sehingga Anda sebagai pengembang tema akan lebih mudah untuk membimbing pengguna tema Anda dengan cara mengarahkan mereka untuk menyunting widget tertentu berdasarkan warna atau pola yang Anda berikan. Sebagai contoh, di sini Saya memberikan warna gradiasi biru pada widget pencarian dan gradiasi kuning pada widget iklan:

body#layout div.widget.BlogSearch div.widget-content {
  background: linear-gradient(#fff, #e8ffff);
}

body#layout div.widget.AdSense div.widget-content {
  background: linear-gradient(#fff, #ffffce);
}

Kode yang Saya beri tanda adalah nama kelas HTML yang akan diterapkan secara otomatis pada widget sesuai dengan jenisnya. Anda juga bisa membuatnya menjadi lebih spesifik lagi dengan memanfaatkan ID widget seperti ini:

body#layout div.widget#BlogSearch1 div.widget-content {
  background: linear-gradient(#fff, #e8ffff);
}

Labels: , , ,

Saturday, July 6, 2019

Kreatif dengan Tag Kondisional pada Widget HTML Blogger

Sebuah widget berjenis HTML pada tema Blogger dapat Anda gunakan sebagai acuan dasar untuk memahami bagaimana cara kerja XML widget, terutama untuk para pengembang yang ingin membuat widget kustom dari kode sumber secara langsung. Berikut ini adalah sebuah contoh:

<b:section class='section-1' id='section-1' name='Bagian 1'>
  <b:widget id='HTML1' title='Judul Widget' type='HTML'>
    <b:widget-settings>
      <b:widget-setting name='content'>Konten widget.</b:widget-setting>
    </b:widget-settings>
    <b:includable id='main'>
      <h3><data:title/></h3>
      <div><data:content/></div>
    </b:includable>
  </b:widget>
</b:section>

Karena elemen <b:widget> harus berada di dalam elemen <b:section>, maka di sini Saya mengilustrasikannya sebagai sebuah widget HTML yang tersemat di dalam seksi bernama Bagian 1. Ketika kode tersebut diterjemahkan sebagai HTML, maka hasilnya akan menjadi tampak seperti ini:

<div class='section-1 section' id='section-1' name='Bagian 1'>
  <div class='widget HTML' data-version='2' id='HTML1'>
    <h3>Judul Widget</h3>
    <div>Konten widget.</div>
  </div>
</div>

Sedangkan pada halaman Tata Letak akan menjadi tampak seperti ini:

Bagian dalam widget Blogger.
Blok dengan judul Bagian 1 telah ditambahkan.

Jika Anda sunting widget tersebut melalui ikon pensil, maka Anda akan menyadari bahwa nilai data:content akan menempati bidang Konten di dalam formulir pengaturan widget. Sedangkan nilai atribut title pada elemen <b:widget> akan menempati bidang Judul (yang nilainya dapat kita tampilkan melalui data:title). Anda bisa memanfaatkan kedua elemen tersebut untuk mengubah perilaku widget berdasarkan nilainya.

Kode di bawah ini akan menampilkan markup HTML judul apabila data judul tidak kosong, dan akan menampilkan markup HTML konten apabila data konten tidak kosong:

<b:if cond='data:title != ""'>
  <h3><data:title/></h3>
</b:if>
<b:if cond='data:content != ""'>
  <div><data:content/></div>
</b:if>

Kode di bawah ini akan menampilkan markup HTML judul dan konten apabila data judul dan data konten tidak kosong:

<b:if cond='data:title != "" && data:content != ""'>
  <h3><data:title/></h3>
  <div><data:content/></div>
</b:if>

Kode di bawah ini akan menambahkan kelas HTML khusus berdasarkan kosong atau tidaknya data judul dan konten:

<div class='container'>

  <b:class cond='data:title != ""' name='has-title'/>
  <b:class cond='data:content != ""' name='has-content'/>

  <h3><data:title/></h3>
  <div><data:content/></div>

</div>

Kode di bawah ini akan menambahkan kelas HTML khusus berdasarkan nilai dari data judul secara spesifik:

<!-- https://getbootstrap.com/docs/4.3/components/alerts -->
<div class='alert' role='alert'>

  <b:comment>language: en</b:comment>
  <b:class cond='data:title in ["Danger", "Error"]' name='alert-danger'/>
  <b:class cond='data:title == "Info"' name='alert-info'/>
  <b:class cond='data:title == "Warning"' name='alert-warning'/>

  <b:comment>language: id</b:comment>
  <b:class cond='data:title in ["Bahaya", "Kesalahan"]' name='alert-danger'/>
  <b:class cond='data:title == "Informasi"' name='alert-info'/>
  <b:class cond='data:title == "Peringatan"' name='alert-warning'/>

  <h4 class='alert-heading'><data:title/></h4>
  <p class='mb-0'><data:content/></p>

</div>

Untuk manipulasi data yang lebih kompleks, Anda bisa memanfaatkan elemen <b:with> untuk mengubah sintaks objek valid yang tertulis di dalam bidang Konten sehingga nilainya dapat diperlakukan sebagai objek nyata di dalam XML widget dengan metode seperti ini.

Labels: , ,

Tuesday, June 25, 2019

Membuat Formulir Kontak dengan Google Drive

Tutorial kali ini merupakan tindak lanjut dari tutorial sejenis yang pernah Saya publikasikan sebelumnya di sini. Sebelum artikel tersebut ditandai sebagai artikel kadaluarsa, layanan Google Drive saat itu masih bernama Google Docs. Saya sempat berpikir untuk memperbarui artikel tersebut saja, tapi karena terdapat unsur historis pada artikel tersebut maka Saya memutuskan untuk membuat artikel yang baru saja.

Tutorial kali ini Saya harap akan jauh lebih mudah untuk diikuti karena di sini Anda tidak lagi membutuhkan ID spreadsheet untuk membuat formulir kontak bekerja. Tidak perlu panjang-panjang, kita langsung ke langkah-langkahnya saja ya!

Membuat Spreadsheet

Berbeda dari tutorial sebelumnya yang dimulai dengan membuat formulir baru, di sini Anda buat spreadsheet-nya terlebih dahulu. Spreadsheet ini nantinya akan kita gunakan sebagai basis-data untuk semua pesan yang berhasil dikirim. Buka https://drive.google.com/drive kemudian klik pada tombol Baru di pojok kiri atas. Pilih Google Spreadsheet kemudian pilih Spreadsheet kosong.

Membuat Spreadsheet Baru
Membuat spreadsheet baru.

Jendela baru akan terbuka untuk memuat editor spreadsheet dari Google. Setelah termuat, klik pada judulnya kemudian ubah namanya menjadi ‘Pesan Masuk’.

Mengganti nama spreadsheet.

Sekarang pilih menu Alat kemudian pilih Buat formulir.

Buat formulir baru.

Tunggu beberapa saat maka akan muncul tab baru di bagian bawah editor dengan label ‘Form Responses 1’.

Sheet baru dibuat secara otomatis.

Klik untuk mengaktifkan sheet tersebut! Anda boleh menghapus sheet sebelumnya karena itu tidak ada gunanya.

Formulir Anda sebenarnya sudah jadi, tapi masih belum berisi apa-apa kecuali satu bidang dengan label Timestamp dan satu bidang lagi yang hanya berfungsi sebagai contoh saja.

Menyunting Formulir

Pilih menu Formulir kemudian pilih Edit formulir. Maka jendela baru akan terbuka untuk memuat editor formulir.

Menyunting formulir.

Pada editor tersebut, hapus atau sunting bidang contoh, kemudian buat bidang-bidang baru dengan rincian seperti berikut:

  • Perihal
    • Tipe: Jawaban singkat
    • Wajib diisi: Ya
  • Nama
    • Tipe: Jawaban singkat
    • Wajib diisi: Ya
  • Surel
    • Tipe: Jawaban singkat
    • Wajib diisi: Ya
  • Situs Web
    • Tipe: Jawaban singkat
    • Wajib diisi: Tidak
  • Pesan
    • Tipe: Paragraf
    • Wajib diisi: Ya

Semua perubahan akan tersimpan secara otomatis.

Membuat Skrip Pemicu

Skrip ini nantinya akan bekerja setiap kali pesan dikirimkan melalui formulir terkait. Untuk membuatnya, pilih menu Alat kemudian pilih Editor skrip. Jendela baru akan terbuka untuk memuat editor skrip.

Membuka editor skrip.

Pada bidang Kode.gs, tempelkan kode ini:

function onFormSubmit(e) {
    GmailApp.sendEmail('you@example.com', 'Tes', JSON.stringify(e));
}

Simpan kode tersebut dengan nama ‘Kirim Pesan’.

Ganti kode yang Saya beri tanda dengan alamat surel Anda. Sekarang pilih menu yang tampak seperti simbol jam untuk mengaktifkan pemicu (biasanya bernama ‘Pemicu proyek saat ini’). Jendela baru akan terbuka untuk memuat halaman pemicu proyek.

Klik tombol Tambahkan Pemicu di pojok kanan bawah. Pada bidang Pilih fungsi yang ingin dijalankan pilih onFormSubmit (sesuai dengan nama fungsi yang kita buat sebelumnya). Pada bidang Pilih penerapan mana yang harus dijalankan pilih Head. Pada bidang Pilih sumber acara pilih Dari Spreadsheet. Pada bidang Pilih jenis acara pilih Saat mengirim formulir. Pada bidang Setelan notifikasi kegagalan ambil pilihan sesuai kebutuhan saja.

Setelah itu tekan tombol Simpan. Anda akan diminta untuk mengautentikasi pemicu tersebut melalui jendela munculan. Ikuti petunjuk yang ada, kemudian tekan tombol Izinkan di akhir tahapan.

Mengirim Pesan

Sekarang kita coba kirimkan pesan. Pilih menu Formulir kemudian pilih Buka formulir langsung. Jendela baru akan terbuka untuk memuat formulir publik Anda. Salin URL pada jendela tersebut untuk dibagikan nanti. Isi semua bidang yang ada kemudian tekan tombol Kirim untuk mengetes. Jika berhasil, maka Anda akan mendapatkan pesan masuk yang kurang lebihnya tampak seperti ini:

Pesan masuk.

Di situ akan tampak semua data yang telah kita kirimkan tadi. Untuk saat ini kita fokus ke bidang values dan namedValues saja. Kita pilih bidang namedValues karena bidang ini memiliki format {"key": "value"} yang lebih mudah untuk diproses.

Buka kembali editor skrip kemudian ubah fungsi onFormSubmit menjadi seperti ini:

function onFormSubmit(e) {
    var to = 'you@example.com',
        values = e.namedValues,
        key, value,
        body = "";
    for (key in values) {
        value = values[key];
        if (typeof value === "object" && value[0]) {
            value = value[0];
        }
        body += key + ': ' + value + '\n';
    }
    GmailApp.sendEmail(to, 'Pesan baru!', body.trim());
}

Simpan perubahan. Kemudian Anda coba kirim pesan lagi, maka format pesan yang masuk akan tampak lebih mudah dibaca sekarang!


Tingkat Lanjut

Mengirim HTML

Pada fungsi sebelumnya, setiap pesan yang dikirim akan diperlakukan sebagai data yang mentah, dimana karakter ganti baris akan diubah menjadi <br> dan tag-tag HTML yang lain akan dianggap sebagai teks biasa. Untuk mengirimkan kode HTML seperti apa adanya, Anda perlu mengosongkan bidang body kemudian ambil parameter berikutnya untuk menempatkan data pada bidang htmlBody agar data yang dikirimkan nantinya dapat dianggap sebagai kode HTML:

GmailApp.sendEmail(to, 'Pesan baru!', "", {
    htmlBody: body
});

Berikut ini adalah fungsi yang telah Saya modifikasi sedemikian rupa agar pesan yang masuk nantinya lebih nyaman untuk dibaca:

function onFormSubmit(e) {
    var to = 'you@example.com',
        values = e.namedValues,
        key, value,
        body = "";
    body += '<table style="width:100%;font-size:13px;font-family:sans-serif;line-height:1.5em;;background:#fff;color:#000;table-layout:fixed;border-collapse:separate;border-spacing:2px;">';
    body += '<tbody>';
    for (key in values) {
        value = values[key];
        if (typeof value === "object" && value[0]) {
            value = value[0];
        }
        // Ubah karakter `\n` menjadi `<br>`
        value = value.replace(/\n/g, '<br>');
        body += '<tr>';
        body += '<th style="font:inherit;font-size:100%;font-weight:bold;width:12em;background:#c3d9ff;padding:.5em 1em;vertical-align:top;text-align:right;">' + key + '</th>';
        body += '<td style="font:inherit;font-size:100%;background:#e0ecff;padding:.5em 1em;vertical-align:top;text-align:left;">' + value + '</td>';
        body += '</tr>';
    }
    body += '</tbody>';
    body += '</table>';
    GmailApp.sendEmail(to, values['Perihal'][0] || 'Pesan Baru!', "", {
        htmlBody: body
    });
}

Mengurutkan Data

Jika pesan yang terkirim tampak tidak sesuai dengan urutan yang seharusnya, Anda bisa mengatur data yang ditampilkan secara manual tanpa melalui pengulangan for:

values['Perihal'] && (body += 'Perihal: ' + values['Perihal'][0] + '\n');
values['Nama'] && (body += 'Nama: ' + values['Nama'][0] + '\n');
values['Surel'] && (body += 'Surel: ' + values['Surel'][0] + '\n');
values['Situs Web'] && (body += 'Situs Web: ' + values['Situs Web'][0] + '\n');
values['Pesan'] && (body += 'Pesan: ' + values['Pesan'][0]);

Atau seperti ini juga bisa:

var orders = ['Perihal', 'Nama', 'Surel', 'Situs Web', 'Pesan'],
    values = e.namedValues,
    key, value,
    body = "";
for (key in orders) {
    if (value = values[key = orders[key]]) {
        if (typeof value === "object" && value[0]) {
            value = value[0];
        }
        body += key + ': ' + value + '\n';
    }
}

Bidang Perihal sebagai Kategori

Anda bisa memanfaatkan fitur editor formulir untuk membatasi bidang yang ada agar hanya bisa diisi oleh nilai-nilai tertentu saja, misalnya dengan memilih tipe bidang Pilihan ganda.

Validasi Alamat Surel

Klik menu tambahan di samping sakelar Wajib diisi kemudian pilih Validasi respons. Pada pilihan Panjang ganti nilainya menjadi Ekspresi reguler (Anda menggunakan Panjang untuk menentukan batasan panjang pesan yang dikirimkan). Dan pada pilihan tambahan di sampingnya ganti nilainya dari Berisi menjadi Kecocokan. Pada bidang Pola, isi dengan ekspresi reguler yang cocok dengan alamat surel (tanpa awalan / dan akhiran /), misalnya seperti ini. Pada bidang Teks kesalahan khusus, isi dengan ‘Pola alamat surel tidak valid.’

Anda juga bisa menerapkan ini pada bidang Situs Web.

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: ,

Thursday, January 17, 2019

Fungsi Pembulatan pada XML Blogger

Pembulatan angka ke bawah:

<b:with value='5.2857' var='input'>
  <b:comment>Convert 5.2857 to 5.0000</b:comment>
  <b:with value='data:input - data:input % 1' var='output'>
    <b:eval expr='data:output'/>
  </b:with>
</b:with>

Referensi: https://www.facebook.com/igoynawamreh/posts/792777201056202

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: , , ,

Monday, September 24, 2018

Google Custom Search API

Google Custom Search
Google Penelusuran kustom.

Implementasi dasar API Google Custom Search untuk menampilkan hasil pencarian pada halaman yang sama tanpa harus memuat ulang halaman, dengan HTML formulir yang dibuat secara terpisah dari hasil penelusuran. Selengkapnya bisa dilihat pada kode sumber.


Referensi: Custom Search Element Control API · Google Custom Search

Labels: , , ,

Wednesday, August 1, 2018

Widget Menu Navigasi Blogger Dari Sumber Berupa Objek

Konsep ini merupakan salah satu penerapan dari artikel berikut sebagai salah satu cara untuk menyederhanakan sintaks HTML elemen navigasi sebagai objek:

<b:widget id='HTML1' title='Navigation' type='HTML' version='1'>
  <b:widget-settings>
    <b:widget-setting name='content'>[{
    name: "Home",
    path: "/"
}]</b:widget-setting>
  </b:widget-settings>
  <b:includable id='main'>
    <b:with expr:value='data:content' var='array'>
      <b:include data='array' name='nav'/>
    </b:with>
  </b:includable>
  <b:includable id='nav' var='array'>
    <nav class='nav' id='nav'>
      <b:include data='array' name='ul'/>
    </nav>
  </b:includable>
  <b:includable id='ul' var='array'>
    <b:if cond='data:array.size > 0'>
      <ul>
        <b:loop values='data:array' var='this'>
          <b:include data='this' name='li'/>
        </b:loop>
      </ul>
    </b:if>
  </b:includable>
  <b:includable id='li' var='this'>
    <li>
      <b:class cond='path(data:view.url, data:this.path) == data:view.url' name='active'/>
      <b:include data='this' name='a'/>
      <b:if cond='data:this.children'>
        <b:include data='this.children' name='ul'/>
      </b:if>
    </li>
  </b:includable>
  <b:includable id='a' var='this'>
    <b:if cond='data:this.path || data:this.link'>
      <a>
        <b:attr expr:value='data:this.path ? path(data:view.url, data:this.path) : data:this.link' name='href'/>
        <b:attr cond='data:this.title' expr:value='data:this.title' name='title'/>
        <b:attr cond='data:this.target' expr:value='data:this.target' name='target'/>
        <data:this.name/>
      </a>
    <b:else/>
      <span class='a'><data:this.name/></span>
    </b:if>
  </b:includable>
</b:widget>

Kode CSS menu navigasi dapat Anda ambil dari sumber mana saja, selama selektor CSS adalah berupa .nav atau #nav (misalnya dari sini). Atau, Anda juga bisa menambahkan kelas kustom seperti ini sebagai cara untuk menyesuaikan kode HTML dengan kode CSS menu navigasi yang Anda dapatkan:

<b:includable id='nav' var='array'>
  <nav class='nav superfish' id='nav'>
    <b:include data='array' name='ul'/>
  </nav>
</b:includable>

Penggunaan

Masuk ke editor HTML Templat, salin dan tempel kode XML di atas ke dalam area <b:section> … </b:section> kemudian simpan perubahan. Anda mungkin perlu mengubah ID widget yang Saya beri tanda dengan urutan angka yang lain untuk mencegah duplikat ID widget. Setelah itu masuk ke editor Tata Letak. Di situ seharusnya sudah bertambah satu buah widget bertipe HTML/JavaScript dengan judul Navigation. Klik tombol Edit!

Setiap item menu tersimpan di dalam kontainer. Berikut ini adalah contoh kontainer menu yang masih kosong:

[]

Kemudian kita tambahkan sebuah menu, sebut saja Beranda:

[{
    name: "Beranda",
    path: "/"
}]

Kemudian kita tambahkan sebuah menu lagi bernama Profil:

[{
    name: "Beranda",
    path: "/"
}, {
    name: "Profil",
    path: "p/tentang-saya.html"
}]

Untuk tautan eksternal, gunakan atribut url sebagai pengganti path:

[{
    name: "Beranda",
    path: "/"
}, {
    name: "Profil",
    url: "//about.me/ta.tau.taufik"
}]

Judul dan target tautan juga dapat ditentukan seperti ini:

[{
    name: "Profil",
    url: "//about.me/ta.tau.taufik",
    title: "Taufik Nurrohman",
    target: "_blank"
}]

Anak-anak menu dapat dibuat kembali pada atribut children dengan aturan yang sama seperti sebelumnya:

[{
    name: "Profil",
    children: [{
        name: "Facebook",
        url: "//www.facebook.com/ta.tau.taufik"
    }, {
        name: "Google+",
        url: "//plus.google.com/+TaufikNurrohman"
    }]
}]

Contoh Objek

[{
    name: "Home",
    path: "/"
}, {
    name: "About",
    path: "p/about.html",
    children: [{
        name: "About Me",
        path: "p/about.html"
    }, {
        name: "Advertise",
        path: "p/advertise.html",
        children: [{
            name: "Method 1",
            path: "p/advertise-1.html"
        }, {
            name: "Method 2",
            path: "p/advertise-2.html"
        }]
    }]
}, {
    name: "Archive",
    path: "p/archive.html"
}, {
    name: "Contact",
    link: "//www.facebook.com/ta.tau.taufik",
    title: "Facebook",
    target: "_blank"
}]

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: , ,